/*
 Navicat Premium Data Transfer

 Source Server         : 腾讯云远程
 Source Server Type    : MySQL
 Source Server Version : 50723
 Source Host           : 118.24.31.230
 Source Database       : xblog

 Target Server Type    : MySQL
 Target Server Version : 50723
 File Encoding         : utf-8

 Date: 08/26/2019 20:14:41 PM
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
--  Table structure for `biz_article`
-- ----------------------------
DROP TABLE IF EXISTS `biz_article`;
CREATE TABLE `biz_article` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `title` varchar(255) DEFAULT NULL COMMENT '文章标题',
  `user_id` bigint(20) unsigned NOT NULL COMMENT '用户ID',
  `cover_image` varchar(255) DEFAULT NULL COMMENT '文章封面图片',
  `qrcode_path` varchar(255) DEFAULT NULL COMMENT '文章专属二维码地址',
  `is_markdown` bit(1) DEFAULT b'1',
  `content` longtext COMMENT '文章内容',
  `content_md` longtext COMMENT 'markdown版的文章内容',
  `top` bit(1) DEFAULT b'0' COMMENT '是否置顶  1置顶 0不置顶',
  `type_id` bigint(20) unsigned NOT NULL COMMENT '类型',
  `status` tinyint(1) unsigned DEFAULT '1' COMMENT '是否删除 1：发布  2:草稿  0:删除',
  `recommended` bit(1) DEFAULT b'0' COMMENT '是否推荐 1推荐 0不推荐',
  `original` bit(1) DEFAULT b'1' COMMENT '是否原创 1原创  0转载',
  `description` varchar(300) DEFAULT NULL COMMENT '文章简介，最多200字',
  `keywords` varchar(200) DEFAULT NULL COMMENT '文章关键字，优化搜索',
  `comment` bit(1) DEFAULT NULL COMMENT '是否开启评论 1开启评论 2不开启',
  `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '添加时间',
  `update_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT COMMENT='文章表';

-- ----------------------------
--  Records of `biz_article`
-- ----------------------------
BEGIN;
INSERT INTO `biz_article` VALUES ('2', '多线程入门', '1', 'xblog/cover/20190130110020297_timg.jpeg', null, b'1', '<blockquote>\n<h2 id=\"-\">多线程入门</h2>\n<ul>\n<li><h4 id=\"-\">线程与进程区别</h4>\n每个正在系统上运行的程序都是一个进程。每个进程包含一到多个线程。线程是一组指令的集合，或者是程序的特殊段，它可以在程序里独立执行。也可以把它理解为代码运行的上下文。所以线程基本上是轻量级的进程，它负责在单个程序里执行多任务。通常由操作系统负责多个线程的调度和执行。<br><br>使用线程可以把占据时间长的程序中的任务放到后台去处理，程序的运行速度可能加快，在一些等待的任务实现上如用户输入、文件读写和网络收发数据等，线程就比较有用了。在这种情况下可以释放一些珍贵的资源如内存占用等等。<br>如果有大量的线程,会影响性能，因为操作系统需要在它们之间切换，更多的线程需要更多的内存空间，线程的中止需要考虑其对程序运行的影响。通常块模型数据是在多个线程间共享的，需要防止线程死锁情况的发生。<br><br><font color=red> 总结:进程是所有线程的集合，每一个线程是进程中的一条执行路径。<br></font><br>多线程的使用目的是提高程序的效率。<br>多线程的几种创建方式。</li>\n<li>第一种继承Thread类 重写run方法</li>\n</ul>\n</blockquote>\n<pre><code class=\"lang-java\">package com.xuyiwei.my_01;\n\n/**\n * Created by xuyiwei on 2019/1/23.\n */\npublic class CreateThread extends Thread  {\n\n    @Override\n    public void run() {\n        for (int i=0;i&lt;100;i++){\n            System.out.print(i+&quot; &quot;);\n        }\n    }\n\n    public static void main(String[] args) {\n        CreateThread c1 = new CreateThread();\n        System.out.println(&quot;多线程启动&quot;);\n        c1.start();\n        System.out.println(&quot;多线程结束&quot;);\n    }\n    /**\n     * 多线程启动\n     多线程结束\n     0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99\n     */\n}\n</code></pre>\n<ol>\n<li>第二种实现Runnable接口,重写run方法</li>\n</ol>\n<pre><code class=\"lang-java\">package com.xuyiwei.my_01;\n\n/**\n * Created by xuyiwei on 2019/1/23.\n */\npublic class CreateRunable implements Runnable {\n    @Override\n    public void run() {\n        for(int i=0;i&lt;10000;i++){\n            System.out.print(i+&quot; &quot;);\n        }\n    }\n\n    public static void main(String[] args) {\n        CreateRunable createRunable = new CreateRunable();\n        System.out.println(&quot;多线程启动&quot;);\n        Thread thread = new Thread(createRunable);\n        Thread thread1 = new Thread(createRunable);\n        thread.start();\n        thread1.start();\n\n        System.out.println(&quot;多线程结束&quot;);\n        System.out.println(Thread.currentThread().getName());\n    }\n}\n</code></pre>\n<ol>\n<li>第三种使用匿名内部类方式</li>\n</ol>\n<pre><code class=\"lang-java\">System.out.println(&quot;-----多线程创建开始-----&quot;);\n         Thread thread = new Thread(new Runnable() {\n            public void run() {\n                for (int i = 0; i&lt; 10; i++) {\n                    System.out.println(&quot;i:&quot; + i);\n                }\n            }\n        });\n thread.start();\n System.out.println(&quot;-----多线程创建结束-----&quot;);\n</code></pre>\n<p>上面三个方式，最好的是实现Runnable接口，因为接口是多个实现的，并不是单个继承的。</p>\n<ul>\n<li><h4 id=\"-\">获取线程对象以及名称</h4>\n</li>\n</ul>\n<table>\n<thead>\n<tr>\n<th style=\"text-align:left\">start()</th>\n<th style=\"text-align:right\">启动线程</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td style=\"text-align:left\">currentThread()</td>\n<td style=\"text-align:right\">获取当前线程对象</td>\n</tr>\n<tr>\n<td style=\"text-align:left\">getID()</td>\n<td style=\"text-align:right\">获取当前线程ID      Thread-编号  该编号从0开始</td>\n</tr>\n<tr>\n<td style=\"text-align:left\">getName()</td>\n<td style=\"text-align:right\">获取当前线程名称</td>\n</tr>\n<tr>\n<td style=\"text-align:left\">sleep(long mill)</td>\n<td style=\"text-align:right\">休眠线程</td>\n</tr>\n<tr>\n<td style=\"text-align:left\">Stop()</td>\n<td style=\"text-align:right\">停止线程</td>\n</tr>\n<tr>\n<td style=\"text-align:left\">Thread()</td>\n<td style=\"text-align:right\">分配一个新的 Thread对象</td>\n</tr>\n<tr>\n<td style=\"text-align:left\">Thread(String name)</td>\n<td style=\"text-align:right\">分配一个新的 Thread对象，具有指定的 name正如其名</td>\n</tr>\n<tr>\n<td style=\"text-align:left\">Thread(Runnable r)</td>\n<td style=\"text-align:right\">分配一个新的 Thread对象</td>\n</tr>\n<tr>\n<td style=\"text-align:left\">Thread(Runnable r,String name)</td>\n<td style=\"text-align:right\">分配一个新的 Thread对象</td>\n</tr>\n</tbody>\n</table>\n<ul>\n<li><h4 id=\"-\">守护线程</h4>\nJava中有两种线程，一种是用户线程，另一种是守护线程。<br><br>用户线程是指用户自定义创建的线程，主线程停止，用户线程不会停止<br>守护线程当进程不存在或主线程停止，守护线程也会被停止。<br><br>使用setDaemon(true)方法设置为守护线程<br></li>\n</ul>\n<pre><code class=\"lang-java\">package com.xuyiwei.my_01;\n\n/**\n * Created by xuyiwei on 2019/1/23.\n */\npublic class DaemonThread {\n    public static void main(String[] args) {\n        Thread thread = new Thread(new Runnable() {\n            @Override\n            public void run() {\n                try {\n                    Thread.sleep(1000);\n                } catch (InterruptedException e) {\n                    e.printStackTrace();\n                }\n                System.out.println(&quot;我是子线程&quot;);\n            }\n        });\n        //守护线程  如果设置成true 则主线程结束他就销毁\n        //如果是用户现场 则主线程结束 他仍然执行 需要注意的是守护线程的设置必须在线程执行之前\n        thread.setDaemon(true);\n        thread.start();\n\n        System.out.println(&quot;我是主线程&quot;);\n        System.out.println(&quot;主线程执行结束&quot;);\n        /**\n         *输出: 我是主线程\n         *主线程执行结束\n         *根据上面的输出发现 我是子线程并没有输出 因为是守护线程  主线程结束 子线程就不再继续执行\n         */\n    }\n}\n</code></pre>\n<ul>\n<li><h2 id=\"-\">多线程运行状态</h2>\n<img src=\"http://xyw-blog.oss-cn-qingdao.aliyuncs.com/xblog/article/20190128221230187_image-1548684750129.png?Expires=1864044750&amp;OSSAccessKeyId=LTAIgFRAJMcFAUPX&amp;Signature=6%2BCP%2BxkLuI0mJyHO8eUQ4HE9E5o%3D\" alt=\"file\"><br><br>线程从创建、运行到结束总是处于下面五个状态之一：新建状态、就绪状态、运行状态、阻塞状态及死亡状态。</li>\n<li><h2 id=\"-\">新建状态</h2>\n当用new操作符创建一个线程时， 例如new Thread(r)，线程还没有开始运行，此时线程处在新建状态。 当一个线程处于新生状态时，程序还没有开始运行线程中的代码</li>\n<li><h2 id=\"-\">就绪状态</h2>\n一个新创建的线程并不自动开始运行，要执行线程，必须调用线程的start()方法。当线程对象调用start()方法即启动了线程，start()方法创建线程运行的系统资源，并调度线程运行run()方法。当start()方法返回后，线程就处于就绪状态。<br><br>处于就绪状态的线程并不一定立即运行run()方法，线程还必须同其他线程竞争CPU时间，只有获得CPU时间才可以运行线程。因为在单CPU的计算机系统中，不可能同时运行多个线程，一个时刻仅有一个线程处于运行状态。因此此时可能有多个线程处于就绪状态。对多个处于就绪状态的线程是由Java运行时系统的线程调度程序(thread scheduler)来调度的。</li>\n<li><h2 id=\"-\">运行状态</h2>\n当线程获得CPU时间后，它才进入运行状态，真正开始执行run()方法.</li>\n<li><h2 id=\"-\">阻塞状态</h2>\n线程运行过程中，可能由于各种原因进入阻塞状态:<br><pre><code>  1&gt;线程通过调用sleep方法进入睡眠状态；&lt;br&gt;\n  2&gt;线程调用一个在I/O上被阻塞的操作，即该操作在输入输出操作完成之前不会返回到它的调用者；最简单的例子是wobsocek的Socket socket = server.accept();  &lt;br&gt;\n  3&gt;线程试图得到一个锁，而该锁正被其他线程持有；&lt;br&gt;\n  4&gt;线程在等待某个触发条件；&lt;br&gt;\n</code></pre></li>\n<li><h2 id=\"-\">死亡状态</h2>\n有两个原因会导致线程死亡：<br><pre><code>  1) run方法正常退出而自然死亡，&lt;br&gt;\n  2) 一个未捕获的异常终止了run方法而使线程猝死。&lt;br&gt;\n  为了确定线程在当前是否存活着（就是要么是可运行的，要么是被阻塞了），需要使用isAlive方法。如果是可运行或被阻塞，这个方法返回true； 如果线程仍旧是new状态且不是可运行的， 或者线程死亡了，则返回false.\n</code></pre></li>\n<li><h2 id=\"join-\">join()方法作用</h2>\njoin作用是让其他线程变为等待,     t1.join();// 让其他线程变为等待，直到当前t1线程执行完毕，才释放。<br><br>thread.Join把指定的线程加入到当前线程，可以将两个交替执行的线程合并为顺序执行的线程。比如在线程B中调用了线程A的Join()方法，直到线程A执行完毕后，才会继续执行线程B。<br></li>\n<li><h2 id=\"-\">需求</h2>\n</li>\n<li>创建一个线程，子线程执行完毕后，主线程才能执行。</li>\n</ul>\n<pre><code class=\"lang-java\">package com.xuyiwei.my_01;\n\n/**\n * Created by xuyiwei on 2019/1/29.\n */\nclass SonJoinDemo implements Runnable{\n\n    @Override\n    public void run() {\n        for(int i=0;i&lt;10;i++){\n            System.out.print(Thread.currentThread().getName()+&quot;  &quot;+i+&quot;   &quot;);\n        }\n    }\n}\n\npublic class JoinDemo1  {\n    public static void main(String[] args) {\n        SonJoinDemo sonJoinDemo = new SonJoinDemo();\n        Thread thread1 = new Thread(sonJoinDemo);\n        Thread thread2 = new Thread(sonJoinDemo);\n        thread1.start();\n        thread2.start();\n        try {\n            //这个join很关键 join的目的 在某个线程中插入join 一定要等join进来的线程执行完 才会执行主线程\n            thread1.join();\n            thread2.join();\n        } catch (InterruptedException e) {\n            e.printStackTrace();\n        }\n        for (int i =0;i&lt;10;i++){\n            System.out.print(&quot;main  &quot;+i+ &quot;  &quot; );\n        }\n\n    }\n}\n</code></pre>\n<ul>\n<li><h2 id=\"-\">优先级</h2>\n现代操作系统基本采用时分的形式调度运行的线程，线程分配得到的时间片的多少决定了线程使用处理器资源的多少，也对应了线程优先级这个概念。在JAVA线程中，通过一个int priority来控制优先级，范围为1-10，其中10最高，默认值为5。下面是源码（基于1.8）中关于priority的一些量和方法。</li>\n</ul>\n<pre><code class=\"lang-java\">package com.xuyiwei.my_01;\n\n/**\n * Created by xuyiwei on 2019/1/29.\n */\nclass PriortyThread implements Runnable{\n    @Override\n    public void run() {\n        for(int i=0;i&lt;100;i++){\n            System.out.print(Thread.currentThread().getName()+&quot; &quot;+i+ &quot; &quot;);\n        }\n    }\n}\npublic class PriortyDemo {\n    public static void main(String[] args) {\n        PriortyThread priortyThread = new PriortyThread();\n        Thread thread1 = new Thread(priortyThread);\n        Thread thread2 = new Thread(priortyThread);\n        thread1.start();\n        //线程最大的优先级是10  但是并不是说优先级越大 他就一定能先执行 他只是占据cpu的可能性变大\n        thread1.setPriority(10);\n        thread2.start();\n    }\n}\n</code></pre>\n<ul>\n<li><h2 id=\"sleep-\">Sleep方法</h2>\n</li>\n</ul>\n<p>线程休眠：sleep()<br>sleep() 定义在Thread.java中。<br>sleep() 的作用是让当前线程休眠，即当前线程会从“运行状态”进入到“休眠(阻塞)状态”。sleep()会指定休眠时间，线程休眠的时间会大于/等于该休眠时间；在线程重新被唤醒时，它会由“阻塞状态”变成“就绪状态”，从而等待cpu的调度执行。但是sleep并不释放锁</p>\n<pre><code class=\"lang-java\">package com.xuyiwei.my_01;\n\n/**\n * Created by xuyiwei on 2019/2/14.\n */\nclass SonThread implements Runnable{\n\n    @Override\n    public void run() {\n        System.out.println(&quot;子线程开始执行&quot;);\n        try {\n            Thread.sleep(1000);\n        } catch (InterruptedException e) {\n            e.printStackTrace();\n        }\n        System.out.println(&quot;子线程执行结束&quot;);\n    }\n}\npublic class ThreadTest01 {\n    public static void main(String[] args) {\n        Thread t1 = new Thread(new SonThread());\n        System.out.println(&quot;主线程开始执行&quot;);\n        t1.start();\n        try {\n            t1.join();\n        } catch (InterruptedException e) {\n            e.printStackTrace();\n        }\n        System.out.println(&quot;主线程运行结束&quot;);\n\n    }\n}\n</code></pre>\n<ul>\n<li><h2 id=\"yield-\">Yield方法</h2>\nThread.yield()方法的作用：暂停当前正在执行的线程，并执行其他线程。（可能没有效果）<br>yield()让当前正在运行的线程回到可运行状态，以允许具有相同优先级的其他线程获得运行的机会。因此，使用yield()的目的是让具有相同优先级的线程之间能够适当的轮换执行。但是，实际中无法保证yield()达到让步的目的，因为，让步的线程可能被线程调度程序再次选中。<br>结论：大多数情况下，yield()将导致线程从运行状态转到可运行状态，但有可能没有效果。yield并不释放锁</li>\n</ul>\n<pre><code class=\"lang-java\">package com.xuyiwei.my_01;\n\npublic class ThreadTest03 {\n    public static void main(String[] args) {\n        Thread t1 = new Thread(new Runnable() {\n            @Override\n            public void run() {\n                System.out.println(Thread.currentThread().getName()+&quot;运行&quot;);\n\n                Thread.yield();\n\n                System.out.println(Thread.currentThread().getName()+&quot;结束&quot;);\n            }\n        });\n        Thread t2 = new Thread(new Runnable() {\n            @Override\n            public void run() {\n                System.out.println(Thread.currentThread().getName()+&quot;运行&quot;);\n\n                System.out.println(Thread.currentThread().getName()+&quot;结束&quot;);\n            }\n        });\n        t1.start(); t2.start();\n        /**\n         Thread-0运行\n         Thread-1运行\n         Thread-1结束\n         Thread-0结束\n         */\n    }\n}\n</code></pre>\n<p>下面的例子证明yield并不释放锁</p>\n<pre><code class=\"lang-java\">package com.xuyiwei.my_01;\n\npublic class ThreadTest04 {\n    static String a = &quot;11&quot;;\n    public static void main(String[] args) {\n        Thread t1 = new Thread(new Runnable() {\n            @Override\n            public void run() {\n                synchronized (a){\n                    System.out.println(Thread.currentThread().getName()+&quot;运行&quot;);\n                    //需要注意的是yield并不释放锁\n                    Thread.yield();\n                    System.out.println(Thread.currentThread().getName()+&quot;结束&quot;);\n                }\n\n            }\n        });\n        Thread t2 = new Thread(new Runnable() {\n            @Override\n            public void run() {\n                synchronized (a) {\n                    System.out.println(Thread.currentThread().getName() + &quot;运行&quot;);\n                    System.out.println(Thread.currentThread().getName() + &quot;结束&quot;);\n                }\n            }\n        });\n        t1.start(); t2.start();\n        /**\n         Thread-0运行\n         Thread-0结束\n         Thread-1运行\n         Thread-1结束\n         */\n    }\n}\n</code></pre>\n<p>运行多少次都是这个结果，因为yield并不释放锁。</p>\n<ul>\n<li><h2 id=\"wait-\">Wait方法</h2>\n</li>\n</ul>\n<p>yield() 与 wait()的比较：<br>我们知道，wait()的作用是让当前线程由“运行状态”进入“等待(阻塞)状态”的同时，也会释放同步锁。而yield()的作用是让步，它也会让当前线程离开“运行状态”。它们的区别是：<br>(1) wait()是让线程由“运行状态”进入到“等待(阻塞)状态”，而不yield()是让线程由“运行状态”进入到“就绪状态”。<br>(2) wait()是会线程释放它所持有对象的同步锁，而yield()方法不会释放锁。</p>\n<ul>\n<li><h2 id=\"notify-\">notify方法</h2>\n</li>\n</ul>\n<p>（1）notify()方法是随机唤醒一个线程，将等待队列中的一个等待线程从等待队列中移到同步队列中。<br>（2）在执行完notify()方法后，当前线程不会马上释放该对象锁，呈wait状态的线程也不能马上获得该对象锁，<br>要等到执行notify方法的线程将程序执行完 ，也就是退出sychronized代码块后，当前线程才会释放锁，<br>而在同步队列中的该线程才可以获取该对象锁。</p>\n<pre><code class=\"lang-java\">package com.xuyiwei.my_01;\n\npublic class ThreadTest04 {\n    static String a = &quot;11&quot;;\n    static String b = &quot;121&quot;;\n\n    public static void main(String[] args) {\n        Thread t1 = new Thread(new Runnable() {\n            @Override\n            public void run() {\n                synchronized (b){\n                    System.out.println(Thread.currentThread().getName()+&quot;运行&quot;);\n                    //需要注意的是yield并不释放锁\n                    try {\n                        Thread.sleep(3000);\n                    } catch (InterruptedException e) {\n                        e.printStackTrace();\n                    }\n                    try {\n                        b.wait();\n                    } catch (InterruptedException e) {\n                        e.printStackTrace();\n                    }\n                    System.out.println(Thread.currentThread().getName()+&quot;结束&quot;);\n                }\n\n            }\n        });\n        Thread t2 = new Thread(new Runnable() {\n            @Override\n            public void run() {\n                synchronized (a) {\n                    System.out.println(Thread.currentThread().getName() + &quot;运行&quot;);\n                    try {\n                        Thread.sleep(3000);\n                    } catch (InterruptedException e) {\n                        e.printStackTrace();\n                    }\n                    //需要注意的是notify唤醒并不释放锁\n                    a.notify();\n                    System.out.println(Thread.currentThread().getName() + &quot;结束&quot;);\n                }\n            }\n        });\n        Thread t3 = new Thread(new Runnable() {\n            @Override\n            public void run() {\n                synchronized (a){\n                    System.out.println(Thread.currentThread().getName()+&quot;运行&quot;);\n                    try {\n                        Thread.sleep(3000);\n                    } catch (InterruptedException e) {\n                        e.printStackTrace();\n                    }\n                    //需要注意的是yield并不释放锁\n                    try {\n                        a.wait();\n                    } catch (InterruptedException e) {\n                        e.printStackTrace();\n                    }\n                    System.out.println(Thread.currentThread().getName()+&quot;结束&quot;);\n                }\n\n            }\n        });\n        t3.setPriority(10);\n        t1.start(); t3.start();t2.start();\n        /**\n         Thread-0运行\n         Thread-1运行\n         Thread-1结束\n         Thread-2运行\n         根据上面的例子  notify唤醒的也是持有该对象锁的线程 并不能唤醒线程0 因此notify和wait本质意义上都是根据加锁的对象而言，并不是一概而论的随机唤醒所有的线程\n         */\n    }\n}\n</code></pre>\n<ul>\n<li><h2 id=\"-\">多线程分批处理数据</h2>\n为了提高数程序的效率，请使用多线程技术分批发送据。<br><br>每开一个线程，都会占用CPU资源<br><br>服务器（电脑）配置 CPU 核数<br></li>\n<li><h2 id=\"-\">练习</h2>\n现在有T1、T2、T3三个线程，你怎样保证T2在T1执行完后执行，T3在T2执行完后执行  </li>\n</ul>\n<pre><code class=\"lang-java\">package com.xuyiwei.my_01;\nclass Test06Thread1 extends Thread{\n    private Test06Thread2 t;\n\n    public Test06Thread1(Test06Thread2 t) {\n        this.t = t;\n    }\n\n    @Override\n    public void run() {\n\n        try {\n            t.join();\n        } catch (InterruptedException e) {\n            e.printStackTrace();\n        }\n        System.out.println(Thread.currentThread().getName()+&quot;开始执行&quot;);\n        System.out.println(Thread.currentThread().getName()+&quot;执行结束&quot;);\n\n    }\n}\nclass Test06Thread2 extends Thread{\n    private Test06Thread3 t;\n\n    public Test06Thread2(Test06Thread3 t) {\n        this.t = t;\n    }\n\n    @Override\n    public void run() {\n\n        try {\n            t.join();\n        } catch (InterruptedException e) {\n            e.printStackTrace();\n        }\n        System.out.println(Thread.currentThread().getName()+&quot;开始执行&quot;);\n        System.out.println(Thread.currentThread().getName()+&quot;执行结束&quot;);\n    }\n}\nclass Test06Thread3 extends Thread{\n    @Override\n    public void run() {\n        try {\n            Thread.sleep(6000);\n        } catch (InterruptedException e) {\n            e.printStackTrace();\n        }\n        System.out.println(Thread.currentThread().getName()+&quot;开始执行&quot;);\n        System.out.println(Thread.currentThread().getName()+&quot;执行结束&quot;);\n    }\n}\npublic class ThreadTest06 {\n    public static void main(String[] args) {\n        Test06Thread3 t3 = new Test06Thread3();\n        t3.start();\n        //注意join的线程一定要启动的线程，才会有效果。不然join相当于没有任何作用。只有t3启动之后，然后t2运行的时候t3.join这样t2才必须等t3运行结束才会运行，不然t2就正常运行\n        Test06Thread2 t2 = new Test06Thread2(t3);\n        t2.start();\n        Test06Thread1 t1= new Test06Thread1(t2);\n        t1.start();\n        t3.setName(&quot;t3&quot;);\n        t2.setName(&quot;t2&quot;);\n        t1.setName(&quot;t1&quot;);\n\n    }\n}\n</code></pre>\n<ul>\n<li><h2 id=\"-\">总结</h2>\n<font color=\'red\'>进程与线程的区别？<br></font><br>答:进程是所有线程的集合，每一个线程是进程中的一条执行路径，线程只是一条执行路径。<br><br><font color=\'red\'>为什么要用多线程？<br></font><br>答:提高程序效率<br><br><font color=\'red\'>多线程创建方式？<br></font><br>答:继承Thread或Runnable 接口。<br><br><font color=\'red\'>是继承Thread类好还是实现Runnable接口好？<br></font><br>答:Runnable接口好，因为实现了接口还可以继续继承。继承Thread类不能再继承。<br><br><font color=\'red\'>你在哪里用到了多线程？<br></font><br>答:主要能体现到多线程提高程序效率。<br><br>举例:分批发送短信、迅雷多线程下载等</li>\n</ul>\n', '> ## 多线程入门\n- ####   线程与进程区别\n每个正在系统上运行的程序都是一个进程。每个进程包含一到多个线程。线程是一组指令的集合，或者是程序的特殊段，它可以在程序里独立执行。也可以把它理解为代码运行的上下文。所以线程基本上是轻量级的进程，它负责在单个程序里执行多任务。通常由操作系统负责多个线程的调度和执行。<br>\n使用线程可以把占据时间长的程序中的任务放到后台去处理，程序的运行速度可能加快，在一些等待的任务实现上如用户输入、文件读写和网络收发数据等，线程就比较有用了。在这种情况下可以释放一些珍贵的资源如内存占用等等。\n如果有大量的线程,会影响性能，因为操作系统需要在它们之间切换，更多的线程需要更多的内存空间，线程的中止需要考虑其对程序运行的影响。通常块模型数据是在多个线程间共享的，需要防止线程死锁情况的发生。<br>\n<font color=red> 总结:进程是所有线程的集合，每一个线程是进程中的一条执行路径。\n</font>   \n多线程的使用目的是提高程序的效率。\n多线程的几种创建方式。\n1. 第一种继承Thread类 重写run方法\n\n```java\npackage com.xuyiwei.my_01;\n\n/**\n * Created by xuyiwei on 2019/1/23.\n */\npublic class CreateThread extends Thread  {\n\n    @Override\n    public void run() {\n        for (int i=0;i<100;i++){\n            System.out.print(i+\" \");\n        }\n    }\n\n    public static void main(String[] args) {\n        CreateThread c1 = new CreateThread();\n        System.out.println(\"多线程启动\");\n        c1.start();\n        System.out.println(\"多线程结束\");\n    }\n    /**\n     * 多线程启动\n     多线程结束\n     0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99\n     */\n}\n```\n\n2. 第二种实现Runnable接口,重写run方法\n\n```java\npackage com.xuyiwei.my_01;\n\n/**\n * Created by xuyiwei on 2019/1/23.\n */\npublic class CreateRunable implements Runnable {\n    @Override\n    public void run() {\n        for(int i=0;i<10000;i++){\n            System.out.print(i+\" \");\n        }\n    }\n\n    public static void main(String[] args) {\n        CreateRunable createRunable = new CreateRunable();\n        System.out.println(\"多线程启动\");\n        Thread thread = new Thread(createRunable);\n        Thread thread1 = new Thread(createRunable);\n        thread.start();\n        thread1.start();\n\n        System.out.println(\"多线程结束\");\n        System.out.println(Thread.currentThread().getName());\n    }\n}\n```\n\n3. 第三种使用匿名内部类方式\n\n```java\nSystem.out.println(\"-----多线程创建开始-----\");\n		 Thread thread = new Thread(new Runnable() {\n			public void run() {\n				for (int i = 0; i< 10; i++) {\n					System.out.println(\"i:\" + i);\n				}\n			}\n		});\n thread.start();\n System.out.println(\"-----多线程创建结束-----\");\n```\n\n上面三个方式，最好的是实现Runnable接口，因为接口是多个实现的，并不是单个继承的。\n- ####   获取线程对象以及名称\n\n| start() | 启动线程 |  \n| :------| ------: |  \n| currentThread() | 获取当前线程对象 |  \n| getID() | 获取当前线程ID      Thread-编号  该编号从0开始 | \n| getName() | 获取当前线程名称 | \n| sleep(long mill) | 休眠线程 | \n| Stop() | 停止线程 | \n| Thread() | 分配一个新的 Thread对象 | \n| Thread(String name) | 分配一个新的 Thread对象，具有指定的 name正如其名 | \n| Thread(Runnable r) | 分配一个新的 Thread对象 | \n| Thread(Runnable r,String name) | 分配一个新的 Thread对象 | \n\n- ####   守护线程\nJava中有两种线程，一种是用户线程，另一种是守护线程。<br>\n用户线程是指用户自定义创建的线程，主线程停止，用户线程不会停止\n守护线程当进程不存在或主线程停止，守护线程也会被停止。<br>\n使用setDaemon(true)方法设置为守护线程<br>\n\n```java\npackage com.xuyiwei.my_01;\n\n/**\n * Created by xuyiwei on 2019/1/23.\n */\npublic class DaemonThread {\n    public static void main(String[] args) {\n        Thread thread = new Thread(new Runnable() {\n            @Override\n            public void run() {\n                try {\n                    Thread.sleep(1000);\n                } catch (InterruptedException e) {\n                    e.printStackTrace();\n                }\n                System.out.println(\"我是子线程\");\n            }\n        });\n        //守护线程  如果设置成true 则主线程结束他就销毁\n        //如果是用户现场 则主线程结束 他仍然执行 需要注意的是守护线程的设置必须在线程执行之前\n        thread.setDaemon(true);\n        thread.start();\n\n        System.out.println(\"我是主线程\");\n        System.out.println(\"主线程执行结束\");\n        /**\n         *输出: 我是主线程\n         *主线程执行结束\n         *根据上面的输出发现 我是子线程并没有输出 因为是守护线程  主线程结束 子线程就不再继续执行\n         */\n    }\n}\n```\n- ## 多线程运行状态\n![file](http://xyw-blog.oss-cn-qingdao.aliyuncs.com/xblog/article/20190128221230187_image-1548684750129.png?Expires=1864044750&OSSAccessKeyId=LTAIgFRAJMcFAUPX&Signature=6%2BCP%2BxkLuI0mJyHO8eUQ4HE9E5o%3D)<br>\n线程从创建、运行到结束总是处于下面五个状态之一：新建状态、就绪状态、运行状态、阻塞状态及死亡状态。\n- ## 新建状态\n当用new操作符创建一个线程时， 例如new Thread(r)，线程还没有开始运行，此时线程处在新建状态。 当一个线程处于新生状态时，程序还没有开始运行线程中的代码\n- ## 就绪状态\n一个新创建的线程并不自动开始运行，要执行线程，必须调用线程的start()方法。当线程对象调用start()方法即启动了线程，start()方法创建线程运行的系统资源，并调度线程运行run()方法。当start()方法返回后，线程就处于就绪状态。<br>\n处于就绪状态的线程并不一定立即运行run()方法，线程还必须同其他线程竞争CPU时间，只有获得CPU时间才可以运行线程。因为在单CPU的计算机系统中，不可能同时运行多个线程，一个时刻仅有一个线程处于运行状态。因此此时可能有多个线程处于就绪状态。对多个处于就绪状态的线程是由Java运行时系统的线程调度程序(thread scheduler)来调度的。\n- ## 运行状态\n当线程获得CPU时间后，它才进入运行状态，真正开始执行run()方法.\n- ## 阻塞状态\n线程运行过程中，可能由于各种原因进入阻塞状态:<br>\n        1>线程通过调用sleep方法进入睡眠状态；<br>\n        2>线程调用一个在I/O上被阻塞的操作，即该操作在输入输出操作完成之前不会返回到它的调用者；最简单的例子是wobsocek的Socket socket = server.accept();  <br>\n        3>线程试图得到一个锁，而该锁正被其他线程持有；<br>\n        4>线程在等待某个触发条件；<br>\n- ## 死亡状态\n有两个原因会导致线程死亡：<br>\n        1) run方法正常退出而自然死亡，<br>\n        2) 一个未捕获的异常终止了run方法而使线程猝死。<br>\n        为了确定线程在当前是否存活着（就是要么是可运行的，要么是被阻塞了），需要使用isAlive方法。如果是可运行或被阻塞，这个方法返回true； 如果线程仍旧是new状态且不是可运行的， 或者线程死亡了，则返回false.\n- ## join()方法作用\njoin作用是让其他线程变为等待, 	t1.join();// 让其他线程变为等待，直到当前t1线程执行完毕，才释放。<br>\nthread.Join把指定的线程加入到当前线程，可以将两个交替执行的线程合并为顺序执行的线程。比如在线程B中调用了线程A的Join()方法，直到线程A执行完毕后，才会继续执行线程B。<br>\n- ## 需求\n1. 创建一个线程，子线程执行完毕后，主线程才能执行。\n\n```java\npackage com.xuyiwei.my_01;\n\n/**\n * Created by xuyiwei on 2019/1/29.\n */\nclass SonJoinDemo implements Runnable{\n\n    @Override\n    public void run() {\n        for(int i=0;i<10;i++){\n            System.out.print(Thread.currentThread().getName()+\"  \"+i+\"   \");\n        }\n    }\n}\n\npublic class JoinDemo1  {\n    public static void main(String[] args) {\n        SonJoinDemo sonJoinDemo = new SonJoinDemo();\n        Thread thread1 = new Thread(sonJoinDemo);\n        Thread thread2 = new Thread(sonJoinDemo);\n        thread1.start();\n        thread2.start();\n        try {\n            //这个join很关键 join的目的 在某个线程中插入join 一定要等join进来的线程执行完 才会执行主线程\n            thread1.join();\n            thread2.join();\n        } catch (InterruptedException e) {\n            e.printStackTrace();\n        }\n        for (int i =0;i<10;i++){\n            System.out.print(\"main  \"+i+ \"  \" );\n        }\n\n    }\n}\n```\n\n- ## 优先级\n现代操作系统基本采用时分的形式调度运行的线程，线程分配得到的时间片的多少决定了线程使用处理器资源的多少，也对应了线程优先级这个概念。在JAVA线程中，通过一个int priority来控制优先级，范围为1-10，其中10最高，默认值为5。下面是源码（基于1.8）中关于priority的一些量和方法。\n\n```java\npackage com.xuyiwei.my_01;\n\n/**\n * Created by xuyiwei on 2019/1/29.\n */\nclass PriortyThread implements Runnable{\n    @Override\n    public void run() {\n        for(int i=0;i<100;i++){\n            System.out.print(Thread.currentThread().getName()+\" \"+i+ \" \");\n        }\n    }\n}\npublic class PriortyDemo {\n    public static void main(String[] args) {\n        PriortyThread priortyThread = new PriortyThread();\n        Thread thread1 = new Thread(priortyThread);\n        Thread thread2 = new Thread(priortyThread);\n        thread1.start();\n        //线程最大的优先级是10  但是并不是说优先级越大 他就一定能先执行 他只是占据cpu的可能性变大\n        thread1.setPriority(10);\n        thread2.start();\n    }\n}\n\n```\n\n- ## Sleep方法\n\n线程休眠：sleep()  \nsleep() 定义在Thread.java中。  \nsleep() 的作用是让当前线程休眠，即当前线程会从“运行状态”进入到“休眠(阻塞)状态”。sleep()会指定休眠时间，线程休眠的时间会大于/等于该休眠时间；在线程重新被唤醒时，它会由“阻塞状态”变成“就绪状态”，从而等待cpu的调度执行。但是sleep并不释放锁\n\n```java\npackage com.xuyiwei.my_01;\n\n/**\n * Created by xuyiwei on 2019/2/14.\n */\nclass SonThread implements Runnable{\n\n    @Override\n    public void run() {\n        System.out.println(\"子线程开始执行\");\n        try {\n            Thread.sleep(1000);\n        } catch (InterruptedException e) {\n            e.printStackTrace();\n        }\n        System.out.println(\"子线程执行结束\");\n    }\n}\npublic class ThreadTest01 {\n    public static void main(String[] args) {\n        Thread t1 = new Thread(new SonThread());\n        System.out.println(\"主线程开始执行\");\n        t1.start();\n        try {\n            t1.join();\n        } catch (InterruptedException e) {\n            e.printStackTrace();\n        }\n        System.out.println(\"主线程运行结束\");\n\n    }\n}\n\n```\n\n\n- ## Yield方法\nThread.yield()方法的作用：暂停当前正在执行的线程，并执行其他线程。（可能没有效果）\nyield()让当前正在运行的线程回到可运行状态，以允许具有相同优先级的其他线程获得运行的机会。因此，使用yield()的目的是让具有相同优先级的线程之间能够适当的轮换执行。但是，实际中无法保证yield()达到让步的目的，因为，让步的线程可能被线程调度程序再次选中。\n结论：大多数情况下，yield()将导致线程从运行状态转到可运行状态，但有可能没有效果。yield并不释放锁\n\n```java\npackage com.xuyiwei.my_01;\n\npublic class ThreadTest03 {\n    public static void main(String[] args) {\n        Thread t1 = new Thread(new Runnable() {\n            @Override\n            public void run() {\n                System.out.println(Thread.currentThread().getName()+\"运行\");\n\n                Thread.yield();\n\n                System.out.println(Thread.currentThread().getName()+\"结束\");\n            }\n        });\n        Thread t2 = new Thread(new Runnable() {\n            @Override\n            public void run() {\n                System.out.println(Thread.currentThread().getName()+\"运行\");\n\n                System.out.println(Thread.currentThread().getName()+\"结束\");\n            }\n        });\n        t1.start(); t2.start();\n        /**\n         Thread-0运行\n         Thread-1运行\n         Thread-1结束\n         Thread-0结束\n         */\n    }\n}\n```\n\n下面的例子证明yield并不释放锁\n```java\npackage com.xuyiwei.my_01;\n\npublic class ThreadTest04 {\n    static String a = \"11\";\n    public static void main(String[] args) {\n        Thread t1 = new Thread(new Runnable() {\n            @Override\n            public void run() {\n                synchronized (a){\n                    System.out.println(Thread.currentThread().getName()+\"运行\");\n                    //需要注意的是yield并不释放锁\n                    Thread.yield();\n                    System.out.println(Thread.currentThread().getName()+\"结束\");\n                }\n\n            }\n        });\n        Thread t2 = new Thread(new Runnable() {\n            @Override\n            public void run() {\n                synchronized (a) {\n                    System.out.println(Thread.currentThread().getName() + \"运行\");\n                    System.out.println(Thread.currentThread().getName() + \"结束\");\n                }\n            }\n        });\n        t1.start(); t2.start();\n        /**\n         Thread-0运行\n         Thread-0结束\n         Thread-1运行\n         Thread-1结束\n         */\n    }\n}\n```\n运行多少次都是这个结果，因为yield并不释放锁。\n\n- ## Wait方法\n\nyield() 与 wait()的比较：\n我们知道，wait()的作用是让当前线程由“运行状态”进入“等待(阻塞)状态”的同时，也会释放同步锁。而yield()的作用是让步，它也会让当前线程离开“运行状态”。它们的区别是：\n(1) wait()是让线程由“运行状态”进入到“等待(阻塞)状态”，而不yield()是让线程由“运行状态”进入到“就绪状态”。\n(2) wait()是会线程释放它所持有对象的同步锁，而yield()方法不会释放锁。\n\n- ## notify方法\n\n（1）notify()方法是随机唤醒一个线程，将等待队列中的一个等待线程从等待队列中移到同步队列中。  \n（2）在执行完notify()方法后，当前线程不会马上释放该对象锁，呈wait状态的线程也不能马上获得该对象锁，\n要等到执行notify方法的线程将程序执行完 ，也就是退出sychronized代码块后，当前线程才会释放锁，\n而在同步队列中的该线程才可以获取该对象锁。\n```java\npackage com.xuyiwei.my_01;\n\npublic class ThreadTest04 {\n    static String a = \"11\";\n    static String b = \"121\";\n\n    public static void main(String[] args) {\n        Thread t1 = new Thread(new Runnable() {\n            @Override\n            public void run() {\n                synchronized (b){\n                    System.out.println(Thread.currentThread().getName()+\"运行\");\n                    //需要注意的是yield并不释放锁\n                    try {\n                        Thread.sleep(3000);\n                    } catch (InterruptedException e) {\n                        e.printStackTrace();\n                    }\n                    try {\n                        b.wait();\n                    } catch (InterruptedException e) {\n                        e.printStackTrace();\n                    }\n                    System.out.println(Thread.currentThread().getName()+\"结束\");\n                }\n\n            }\n        });\n        Thread t2 = new Thread(new Runnable() {\n            @Override\n            public void run() {\n                synchronized (a) {\n                    System.out.println(Thread.currentThread().getName() + \"运行\");\n                    try {\n                        Thread.sleep(3000);\n                    } catch (InterruptedException e) {\n                        e.printStackTrace();\n                    }\n                    //需要注意的是notify唤醒并不释放锁\n                    a.notify();\n                    System.out.println(Thread.currentThread().getName() + \"结束\");\n                }\n            }\n        });\n        Thread t3 = new Thread(new Runnable() {\n            @Override\n            public void run() {\n                synchronized (a){\n                    System.out.println(Thread.currentThread().getName()+\"运行\");\n                    try {\n                        Thread.sleep(3000);\n                    } catch (InterruptedException e) {\n                        e.printStackTrace();\n                    }\n                    //需要注意的是yield并不释放锁\n                    try {\n                        a.wait();\n                    } catch (InterruptedException e) {\n                        e.printStackTrace();\n                    }\n                    System.out.println(Thread.currentThread().getName()+\"结束\");\n                }\n\n            }\n        });\n        t3.setPriority(10);\n        t1.start(); t3.start();t2.start();\n        /**\n         Thread-0运行\n         Thread-1运行\n         Thread-1结束\n         Thread-2运行\n         根据上面的例子  notify唤醒的也是持有该对象锁的线程 并不能唤醒线程0 因此notify和wait本质意义上都是根据加锁的对象而言，并不是一概而论的随机唤醒所有的线程\n         */\n    }\n}\n\n```\n\n- ## 多线程分批处理数据\n为了提高数程序的效率，请使用多线程技术分批发送据。<br>\n每开一个线程，都会占用CPU资源<br>\n服务器（电脑）配置 CPU 核数<br>\n- ## 练习\n现在有T1、T2、T3三个线程，你怎样保证T2在T1执行完后执行，T3在T2执行完后执行  \n\n```java\npackage com.xuyiwei.my_01;\nclass Test06Thread1 extends Thread{\n    private Test06Thread2 t;\n\n    public Test06Thread1(Test06Thread2 t) {\n        this.t = t;\n    }\n\n    @Override\n    public void run() {\n\n        try {\n            t.join();\n        } catch (InterruptedException e) {\n            e.printStackTrace();\n        }\n        System.out.println(Thread.currentThread().getName()+\"开始执行\");\n        System.out.println(Thread.currentThread().getName()+\"执行结束\");\n\n    }\n}\nclass Test06Thread2 extends Thread{\n    private Test06Thread3 t;\n\n    public Test06Thread2(Test06Thread3 t) {\n        this.t = t;\n    }\n\n    @Override\n    public void run() {\n\n        try {\n            t.join();\n        } catch (InterruptedException e) {\n            e.printStackTrace();\n        }\n        System.out.println(Thread.currentThread().getName()+\"开始执行\");\n        System.out.println(Thread.currentThread().getName()+\"执行结束\");\n    }\n}\nclass Test06Thread3 extends Thread{\n    @Override\n    public void run() {\n        try {\n            Thread.sleep(6000);\n        } catch (InterruptedException e) {\n            e.printStackTrace();\n        }\n        System.out.println(Thread.currentThread().getName()+\"开始执行\");\n        System.out.println(Thread.currentThread().getName()+\"执行结束\");\n    }\n}\npublic class ThreadTest06 {\n    public static void main(String[] args) {\n        Test06Thread3 t3 = new Test06Thread3();\n        t3.start();\n        //注意join的线程一定要启动的线程，才会有效果。不然join相当于没有任何作用。只有t3启动之后，然后t2运行的时候t3.join这样t2才必须等t3运行结束才会运行，不然t2就正常运行\n        Test06Thread2 t2 = new Test06Thread2(t3);\n        t2.start();\n        Test06Thread1 t1= new Test06Thread1(t2);\n        t1.start();\n        t3.setName(\"t3\");\n        t2.setName(\"t2\");\n        t1.setName(\"t1\");\n\n    }\n}\n\n\n```\n- ## 总结\n<font color=\'red\'>进程与线程的区别？<br></font>\n答:进程是所有线程的集合，每一个线程是进程中的一条执行路径，线程只是一条执行路径。<br>\n<font color=\'red\'>为什么要用多线程？<br></font>\n答:提高程序效率<br>\n<font color=\'red\'>多线程创建方式？<br></font>\n答:继承Thread或Runnable 接口。<br>\n<font color=\'red\'>是继承Thread类好还是实现Runnable接口好？<br></font>\n答:Runnable接口好，因为实现了接口还可以继续继承。继承Thread类不能再继承。<br>\n<font color=\'red\'>你在哪里用到了多线程？<br></font>\n答:主要能体现到多线程提高程序效率。<br>\n举例:分批发送短信、迅雷多线程下载等', b'0', '2', '1', b'0', b'0', '多线程的入门', 'java  多线程', b'1', '2019-01-30 03:00:40', '2019-01-30 03:00:40'), ('3', '线程安全', '1', 'xblog/cover/20190203151358162_timg.jpeg', null, b'1', '<ul>\n<li><h2 id=\"-\">什么是线程安全</h2>\n</li>\n<li><h4 id=\"-\">为什么会有线程安全的问题</h4>\n&nbsp;&nbsp;&nbsp;&nbsp;当多个线程同时共享，同一个全局变量或静态变量，做写的操作时，可能会发生数据冲突问题，也就是线程安全问题。但是做读操作是不会发生数据冲突问题。</li>\n</ul>\n<p>案例:需求现在有100张火车票，有两个窗口同时抢火车票，请使用多线程模拟抢票效果。</p>\n<pre><code class=\"lang-java\">package com.xuyiwei.my_02;\nclass TicketWindow implements Runnable{\n    private static int i = 100;\n    private static Object object = new Object();\n    @Override\n    public void run() {\n        while (TicketWindow.i&gt;0){\n            //只要确保同一时间只有一个线程在操作数据即可\n            synchronized (object){\n                if(i--&gt;0){\n                    System.out.print(Thread.currentThread().getName()+&quot;卖出一张车票，还剩&quot;+i+&quot;张      &quot;);\n                }\n            }\n        }\n    }\n}\npublic class TicketDemo01 {\n    public static void main(String[] args) {\n        TicketWindow window = new TicketWindow();\n        for(int z=0;z&lt;10;z++){\n            Thread t = new Thread(window);\n            t.start();\n        }\n    }\n    /**\n     * Thread-0卖出一张车票，还剩99张      Thread-0卖出一张车票，还剩98张      Thread-0卖出一张车票，还剩97张      Thread-0卖出一张车票，还剩96张      Thread-0卖出一张车票，还剩95张      Thread-0卖出一张车票，还剩94张      Thread-0卖出一张车票，还剩93张      Thread-0卖出一张车票，还剩92张      Thread-0卖出一张车票，还剩91张      Thread-0卖出一张车票，还剩90张      Thread-0卖出一张车票，还剩89张      Thread-0卖出一张车票，还剩88张      Thread-0卖出一张车票，还剩87张      Thread-0卖出一张车票，还剩86张      Thread-0卖出一张车票，还剩85张      Thread-0卖出一张车票，还剩84张      Thread-0卖出一张车票，还剩83张      Thread-0卖出一张车票，还剩82张      Thread-0卖出一张车票，还剩81张      Thread-0卖出一张车票，还剩80张      Thread-0卖出一张车票，还剩79张      Thread-0卖出一张车票，还剩78张      Thread-0卖出一张车票，还剩77张      Thread-0卖出一张车票，还剩76张      Thread-0卖出一张车票，还剩75张      Thread-0卖出一张车票，还剩74张      Thread-0卖出一张车票，还剩73张      Thread-0卖出一张车票，还剩72张      Thread-0卖出一张车票，还剩71张      Thread-0卖出一张车票，还剩70张      Thread-0卖出一张车票，还剩69张      Thread-0卖出一张车票，还剩68张      Thread-0卖出一张车票，还剩67张      Thread-0卖出一张车票，还剩66张      Thread-0卖出一张车票，还剩65张      Thread-0卖出一张车票，还剩64张      Thread-0卖出一张车票，还剩63张      Thread-0卖出一张车票，还剩62张      Thread-0卖出一张车票，还剩61张      Thread-0卖出一张车票，还剩60张      Thread-0卖出一张车票，还剩59张      Thread-0卖出一张车票，还剩58张      Thread-0卖出一张车票，还剩57张      Thread-0卖出一张车票，还剩56张      Thread-0卖出一张车票，还剩55张      Thread-0卖出一张车票，还剩54张      Thread-0卖出一张车票，还剩53张      Thread-0卖出一张车票，还剩52张      Thread-0卖出一张车票，还剩51张      Thread-0卖出一张车票，还剩50张      Thread-0卖出一张车票，还剩49张      Thread-0卖出一张车票，还剩48张      Thread-0卖出一张车票，还剩47张      Thread-0卖出一张车票，还剩46张      Thread-0卖出一张车票，还剩45张      Thread-0卖出一张车票，还剩44张      Thread-0卖出一张车票，还剩43张      Thread-0卖出一张车票，还剩42张      Thread-0卖出一张车票，还剩41张      Thread-0卖出一张车票，还剩40张      Thread-0卖出一张车票，还剩39张      Thread-0卖出一张车票，还剩38张      Thread-0卖出一张车票，还剩37张      Thread-0卖出一张车票，还剩36张      Thread-0卖出一张车票，还剩35张      Thread-0卖出一张车票，还剩34张      Thread-0卖出一张车票，还剩33张      Thread-0卖出一张车票，还剩32张      Thread-0卖出一张车票，还剩31张      Thread-1卖出一张车票，还剩30张      Thread-1卖出一张车票，还剩29张      Thread-1卖出一张车票，还剩28张      Thread-1卖出一张车票，还剩27张      Thread-1卖出一张车票，还剩26张      Thread-1卖出一张车票，还剩25张      Thread-1卖出一张车票，还剩24张      Thread-1卖出一张车票，还剩23张      Thread-1卖出一张车票，还剩22张      Thread-1卖出一张车票，还剩21张      Thread-1卖出一张车票，还剩20张      Thread-1卖出一张车票，还剩19张      Thread-1卖出一张车票，还剩18张      Thread-1卖出一张车票，还剩17张      Thread-1卖出一张车票，还剩16张      Thread-1卖出一张车票，还剩15张      Thread-1卖出一张车票，还剩14张      Thread-1卖出一张车票，还剩13张      Thread-1卖出一张车票，还剩12张      Thread-1卖出一张车票，还剩11张      Thread-1卖出一张车票，还剩10张      Thread-1卖出一张车票，还剩9张      Thread-1卖出一张车票，还剩8张      Thread-1卖出一张车票，还剩7张      Thread-1卖出一张车票，还剩6张      Thread-1卖出一张车票，还剩5张      Thread-1卖出一张车票，还剩4张      Thread-1卖出一张车票，还剩3张      Thread-1卖出一张车票，还剩2张      Thread-1卖出一张车票，还剩1张      Thread-1卖出一张车票，还剩0张      \n     */\n}\n</code></pre>\n<p>如果不加synchronized，会导致两个窗口同时卖同一张车票，容易出现发生数据冲突。</p>\n<ul>\n<li><h2 id=\"-\">线程安全的解决办法</h2>\n</li>\n</ul>\n<font color=\'red\'>问:如何解决多线程之间线程安全问题?<br></font><br>答:使用多线程之间同步synchronized或使用锁(lock)。<br><br><br><font color=\'red\'>问:为什么使用线程同步或使用锁能解决线程安全问题呢？<br></font><br>答:将可能会发生数据冲突问题(线程不安全问题)，只能让当前一个线程进行执行。代码执行完成后释放锁，让后才能让其他线程进行执行。这样的话就可以解决线程不安全问题。<br><br><br><font color=\'red\'>问:什么是多线程之间同步？<br></font><br><font >答:当多个线程共享同一个资源,不会受到其他线程的干扰。</font>\n\n<ul>\n<li><h2 id=\"lock-\">Lock锁</h2>\n首先要说明的就是Lock，通过查看Lock的源码可知，Lock是一个接口：</li>\n</ul>\n<pre><code class=\"lang-java\">public interface Lock {\n    void lock();\n    void lockInterruptibly() throws InterruptedException;\n    boolean tryLock();\n    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;\n    void unlock();\n    Condition newCondition();\n}\n</code></pre>\n<p>　下面来逐个讲述Lock接口中每个方法的使用，lock()、tryLock()、tryLock(long time, TimeUnit unit)和lockInterruptibly()是用来获取锁的。unLock()方法是用来释放锁的。newCondition()这个方法暂且不在此讲述，会在后面的线程协作一文中讲述。</p>\n<p>　　在Lock中声明了四个方法来获取锁，那么这四个方法有何区别呢？</p>\n<p>　　首先lock()方法是平常使用得最多的一个方法，就是用来获取锁。如果锁已被其他线程获取，则进行等待。</p>\n<p>　　由于在前面讲到如果采用Lock，必须主动去释放锁，并且在发生异常时，不会自动释放锁。因此一般来说，使用Lock必须在try{}catch{}块中进行，并且将释放锁的操作放在finally块中进行，以保证锁一定被被释放，防止死锁的发生。通常使用Lock来进行同步的话，是以下面这种形式去使用的：</p>\n<pre><code class=\"lang-java\">Lock lock = ...;\nlock.lock();\ntry{\n    //处理任务\n}catch(Exception ex){\n\n}finally{\n    lock.unlock();   //释放锁\n}\n</code></pre>\n<p>　tryLock()方法是有返回值的，它表示用来尝试获取锁，如果获取成功，则返回true，如果获取失败（即锁已被其他线程获取），则返回false，也就说这个方法无论如何都会立即返回。在拿不到锁时不会一直在那等待。</p>\n<p>　　tryLock(long time, TimeUnit unit)方法和tryLock()方法是类似的，只不过区别在于这个方法在拿不到锁时会等待一定的时间，在时间期限之内如果还拿不到锁，就返回false。如果如果一开始拿到锁或者在等待期间内拿到了锁，则返回true。</p>\n<p>　　所以，一般情况下通过tryLock来获取锁时是这样使用的：</p>\n<pre><code class=\"lang-java\">Lock lock = ...;\nif(lock.tryLock()) {\n     try{\n         //处理任务\n     }catch(Exception ex){\n\n     }finally{\n         lock.unlock();   //释放锁\n     } \n}else {\n    //如果不能获取锁，则直接做其他事情\n}\n</code></pre>\n<p>　lockInterruptibly()方法比较特殊，当通过这个方法去获取锁时，如果线程正在等待获取锁，则这个线程能够响应中断，即中断线程的等待状态。也就使说，当两个线程同时通过lock.lockInterruptibly()想获取某个锁时，假若此时线程A获取到了锁，而线程B只有在等待，那么对线程B调用threadB.interrupt()方法能够中断线程B的等待过程。</p>\n<p>　　由于lockInterruptibly()的声明中抛出了异常，所以lock.lockInterruptibly()必须放在try块中或者在调用lockInterruptibly()的方法外声明抛出InterruptedException。</p>\n<p>　　因此lockInterruptibly()一般的使用形式如下：\n　　</p>\n<pre><code class=\"lang-java\">package com.xuyiwei.my_02;\n\n/**\n * Created by xuyiwei on 2019/2/14.\n */\nimport java.util.concurrent.locks.Lock;\nimport java.util.concurrent.locks.ReentrantLock;\n\nclass BussinessClass {\n\n    private Lock lock = new ReentrantLock();\n\n    // 业务方法\n    public void bFuction() {\n        String tName = Thread.currentThread().getName();\n        try {\n            System.out.println(tName + &quot;-开始获取锁..........&quot;);\n            //这种方式获取的锁是可以被中断的  lock相对于synchronized有这个好处 但是lock锁住的都是对象本身\n            lock.lockInterruptibly();\n\n            System.out.println(tName + &quot;-获取到锁了！！！！&quot;);\n            System.out.println(tName + &quot;-睡觉了，睡个30秒！&quot;);\n            Thread.sleep(30000);\n            System.out.println(tName + &quot;-睡醒了，干活！&quot;);\n            for (int i = 0; i &lt; 5; i++) {\n                System.out.println(tName + &quot;：&quot; + i);\n            }\n        } catch (Exception e) {\n            System.out.println(tName + &quot;-我好像被中断了！&quot;);\n            e.printStackTrace();\n        } finally {\n            lock.unlock();\n            System.out.println(tName + &quot;-释放了锁&quot;);\n        }\n    }\n}\npublic class LockTest3 {\n    public static void main(String[] args) {\n        BussinessClass bc = new BussinessClass();\n        Thread t1 = new Thread(new Runnable() {\n            @Override\n            public void run() {\n                bc.bFuction();\n            }\n        });\n        Thread t2 = new Thread(new Runnable() {\n            @Override\n            public void run() {\n                bc.bFuction();\n            }\n        });\n        t1.start();\n        t2.start();\n        //这里可以中断lockInterruptibly加锁的线程\n        t1.interrupt();\n        /**\n         * Thread-0-开始获取锁..........\n         Thread-1-开始获取锁..........\n         Thread-0-获取到锁了！！！！\n         Thread-0-睡觉了，睡个30秒！\n         Thread-0-我好像被中断了！\n         Thread-0-释放了锁\n         Thread-1-获取到锁了！！！！\n         Thread-1-睡觉了，睡个30秒！\n         java.lang.InterruptedException: sleep interrupted\n         at java.lang.Thread.sleep(Native Method)\n         at com.xuyiwei.my_02.BussinessClass.bFuction(LockTest3.java:26)\n         at com.xuyiwei.my_02.LockTest3$1.run(LockTest3.java:46)\n         at java.lang.Thread.run(Thread.java:744)\n         Thread-1-睡醒了，干活！\n         Thread-1：0\n         Thread-1：1\n         Thread-1：2\n         Thread-1：3\n         Thread-1：4\n         Thread-1-释放了锁\n\n         */\n    }\n}\n</code></pre>\n<p>　注意，当一个线程获取了锁之后，是不会被interrupt()方法中断的。因为本身在前面的文章中讲过单独调用interrupt()方法不能中断正在运行过程中的线程，只能中断阻塞过程中的线程。</p>\n<p>　　因此当通过lockInterruptibly()方法获取某个锁时，如果不能获取到，只有进行等待的情况下，是可以响应中断的。</p>\n<p>　　而用synchronized修饰的话，当一个线程处于等待某个锁的状态，是无法被中断的，只有一直等待下去。</p>\n<ul>\n<li><h2 id=\"-\">同步代码块</h2>\n</li>\n</ul>\n<p>什么是同步代码块？<br><br>答:就是将可能会发生线程安全问题的代码，给包括起来。<br><br>synchronized(同一个数据){<br><br>    可能会发生线程冲突问题<br><br>}<br><br>就是同步代码块 <br><br>synchronized(对象)//这个对象可以为任意对象 <br><br>{ <br><br>    需要被同步的代码 <br><br>} <br><br> <br><br>对象如同锁，持有锁的线程可以在同步中执行 <br><br>没持有锁的线程即使获取CPU的执行权，也进不去 <br><br>同步的前提： <br><br>1，必须要有两个或者两个以上的线程 <br><br>2，必须是多个线程使用同一个锁 <br><br>必须保证同步中只能有一个线程在运行 <br><br>好处：解决了多线程的安全问题 <br><br>弊端：多个线程需要判断锁，较为消耗资源、抢锁的资源。 <br></p>\n<pre><code class=\"lang-java\">package com.xuyiwei.my_02;\n\n/**\n * Created by xuyiwei on 2019/1/30.\n */\npublic class SynchronizedDemo implements Runnable{\n    private static Object obj = new Object();\n    @Override\n    public void run() {\n        synchronized (SynchronizedDemo.obj){\n            for(int i=0;i&lt;50;i++){\n                System.out.print(Thread.currentThread().getName()+&quot;:&quot;+i+&quot;  &quot;);\n            }\n        }\n    }\n\n    public static void main(String[] args) {\n        SynchronizedDemo synchronizedDemo = new SynchronizedDemo();\n        Thread thread1 = new Thread(synchronizedDemo);\n        Thread thread2 = new Thread(synchronizedDemo);\n        thread1.start();\n        thread2.start();\n        /**\n         Thread-0:0  Thread-0:1  Thread-0:2  Thread-0:3  Thread-0:4  Thread-0:5  Thread-0:6  Thread-0:7  Thread-0:8  Thread-0:9  Thread-0:10  Thread-0:11  Thread-0:12  Thread-0:13  Thread-0:14  Thread-0:15  Thread-0:16  Thread-0:17  Thread-0:18  Thread-0:19  Thread-0:20  Thread-0:21  Thread-0:22  Thread-0:23  Thread-0:24  Thread-0:25  Thread-0:26  Thread-0:27  Thread-0:28  Thread-0:29  Thread-0:30  Thread-0:31  Thread-0:32  Thread-0:33  Thread-0:34  Thread-0:35  Thread-0:36  Thread-0:37  Thread-0:38  Thread-0:39  Thread-0:40  Thread-0:41  Thread-0:42  Thread-0:43  Thread-0:44  Thread-0:45  Thread-0:46  Thread-0:47  Thread-0:48  Thread-0:49  Thread-1:0  Thread-1:1  Thread-1:2  Thread-1:3  Thread-1:4  Thread-1:5  Thread-1:6  Thread-1:7  Thread-1:8  Thread-1:9  Thread-1:10  Thread-1:11  Thread-1:12  Thread-1:13  Thread-1:14  Thread-1:15  Thread-1:16  Thread-1:17  Thread-1:18  Thread-1:19  Thread-1:20  Thread-1:21  Thread-1:22  Thread-1:23  Thread-1:24  Thread-1:25  Thread-1:26  Thread-1:27  Thread-1:28  Thread-1:29  Thread-1:30  Thread-1:31  Thread-1:32  Thread-1:33  Thread-1:34  Thread-1:35  Thread-1:36  Thread-1:37  Thread-1:38  Thread-1:39  Thread-1:40  Thread-1:41  Thread-1:42  Thread-1:43  Thread-1:44  Thread-1:45  Thread-1:46  Thread-1:47  Thread-1:48  Thread-1:49\n         */\n    }\n}\n</code></pre>\n<ul>\n<li><h2 id=\"-\">同步函数</h2>\n什么是同步函数？<br><br>答：在方法上修饰synchronized 称为同步函数<br></li>\n</ul>\n<p>同步函数本质上使用了this锁</p>\n<pre><code class=\"lang-java\">package com.xuyiwei.my_02;\n\n/**\n * Created by xuyiwei on 2019/1/30.\n */\npublic class SynchronizedMethod implements Runnable {\n    private boolean flag = true;\n    @Override\n    public void run() {\n        if(flag){\n            synchronized (this){\n                for(int i=0;i&lt;20;i++){\n                    System.out.print(Thread.currentThread().getName()+&quot;:&quot;+i+&quot; &quot;);\n                }\n                System.out.println();\n            }\n        }else{\n            sale();\n        }\n    }\n\n    public synchronized void sale(){\n        for(int i=0;i&lt;20;i++){\n            System.out.print(Thread.currentThread().getName()+&quot;:&quot;+i+&quot; &quot;);\n        }\n        System.out.println();\n    }\n\n\n    public boolean isFlag() {\n        return flag;\n    }\n\n    public void setFlag(boolean flag) {\n        this.flag = flag;\n    }\n\n    public static void main(String[] args) {\n        SynchronizedMethod synchronizedMethod = new SynchronizedMethod();\n        Thread thread1 = new Thread(synchronizedMethod,&quot;1号线程&quot;);\n        Thread thread2 = new Thread(synchronizedMethod,&quot;2号线程&quot;);\n        thread1.start();\n        synchronizedMethod.setFlag(false);\n        thread2.start();\n        /**\n         1号线程:0 1号线程:1 1号线程:2 1号线程:3 1号线程:4 1号线程:5 1号线程:6 1号线程:7 1号线程:8 1号线程:9 1号线程:10 1号线程:11 1号线程:12 1号线程:13 1号线程:14 1号线程:15 1号线程:16 1号线程:17 1号线程:18 1号线程:19\n         2号线程:0 2号线程:1 2号线程:2 2号线程:3 2号线程:4 2号线程:5 2号线程:6 2号线程:7 2号线程:8 2号线程:9 2号线程:10 2号线程:11 2号线程:12 2号线程:13 2号线程:14 2号线程:15 2号线程:16 2号线程:17 2号线程:18 2号线程:19           证明主线程和子线程占用的是同一个锁，因为没有起冲突\n         */\n    }\n}\n</code></pre>\n<ul>\n<li><h2 id=\"-\">静态同步函数</h2>\n答：什么是静态同步函数？<br><br>方法上加上static关键字，使用synchronized 关键字修饰 或者使用类.class文件。<br><br>静态的同步函数使用的锁是  该函数所属字节码文件对象<br>可以用 getClass方法获取，也可以用当前  类名.class 表示。</li>\n</ul>\n<pre><code class=\"lang-java\">package com.xuyiwei.my_02;\n\n/**\n * Created by xuyiwei on 2019/1/30.\n */\npublic class ClassSynchronized implements Runnable{\n    int trainCount = 100;\n    @Override\n    public void run() {\n        synchronized (ClassSynchronized.class) {\n            System.out.println(Thread.currentThread().getName() + &quot;,出售 第&quot; + (100 - trainCount + 1) + &quot;张票.&quot;);\n            trainCount--;\n            try {\n                Thread.sleep(100);\n            } catch (Exception e) {\n            }\n        }\n    }\n}\n</code></pre>\n<ul>\n<li><h2 id=\"-\">多线程死锁</h2>\n</li>\n<li><h4 id=\"-\">什么是多线程死锁？</h4>\n同步中嵌套同步,导致锁无法释放</li>\n</ul>\n<pre><code class=\"lang-java\">package com.xuyiwei.my_02;\n\n/**\n * Created by xuyiwei on 2019/1/30.\n */\npublic class DeadLocked implements Runnable {\n    private static Object obj = new Object();\n    public boolean flag = true;\n    @Override\n    public void run() {\n        if(flag){\n            /**\n             * 这里死锁的原因 1号线程需要执行，先需要获得obj锁，然后在获得this锁\n             * 2号线程执行，需要先获得this锁，然后获得obj锁\n             * 当1号线程占据obj锁后进入睡眠，2号线程占据this锁之后进入睡眠，但是两个线程都需要去等对方释放锁才能进行下一步\n             * 因此变成了死锁\n             */\n            synchronized (obj){\n                try {\n                    Thread.sleep(1000);\n                } catch (InterruptedException e) {\n                    e.printStackTrace();\n                }\n\n                synchronized(this){\n                    for(int i=0;i&lt;10000;i++){\n                        System.out.print(&quot;run:&quot;+i+&quot;  &quot;);\n                    }\n                }\n            }\n        }else{\n            try {\n                sale();\n            } catch (InterruptedException e) {\n                e.printStackTrace();\n            }\n        }\n\n    }\n\n    public synchronized void sale() throws InterruptedException {\n        synchronized (obj){\n            Thread.sleep(40);\n\n            for(int i=0;i&lt;1000;i++){\n                System.out.print(&quot;sale:&quot;+i+&quot;  &quot;);\n            }\n        }\n    }\n\n    public static void main(String[] args) throws InterruptedException {\n        DeadLocked deadLocked = new DeadLocked();\n        Thread thread = new Thread(deadLocked);\n        Thread thread1 = new Thread(deadLocked);\n        thread.start();\n        //这里必须要睡眠 因为 deadLocked.flag设置成false的时候 有可能线程还没进入true的方法  然后1号死锁线程也需要睡眠，确保二号线程开启之前 她已经占据锁了 这样才能模拟死锁\n        Thread.sleep(40);\n        deadLocked.flag = false;\n        thread1.start();\n    }\n}\n</code></pre>\n<ul>\n<li><h2 id=\"-\">多线程的三大特性</h2>\n原子性、可见性、有序性</li>\n<li><h4 id=\"-\">什么是原子性</h4>\n<p>即一个操作或者多个操作<br>要么全部执行并且执行的过程不会被任何因素打断，要么就都不执行。<br>一个很经典的例子就是银行账户转账问题：<br>比如从账户A向账户B转1000元，那么必然包括2个操作：从账户A减去1000元，往账户B加上1000元。这2个操作必须要具备原子性才能保证不出现一些意外的问题。<br>我们操作数据也是如此，比如i=i+1；其中就包括，读取i的值，计算i，写入i。这行代码在Java中是不具备原子性的，则多线程运行肯定会出问题，所以也需要我们使用同步和lock这些东西来确保这个特性了。<br>原子性其实就是保证数据一致、线程安全一部分。</p>\n</li>\n<li><h4 id=\"-\">什么是可见性</h4>\n<p>当多个线程访问同一个变量时，一个线程修改了这个变量的值，其他线程能够立即看得到修改的值。<br>若两个线程在不同的cpu，那么线程1改变了i的值还没刷新到主存，线程2又使用了i，那么这个i值肯定还是之前的，线程1对变量的修改线程没看到这就是可见性问题。 </p>\n</li>\n<li><h4 id=\"-\">什么是有序性</h4>\n<p>程序执行的顺序按照代码的先后顺序执行。<br>一般来说处理器为了提高程序运行效率，可能会对输入代码进行优化，它不保证程序中各个语句的执行先后顺序同代码中的顺序一致，但是它会保证程序最终执行结果和代码顺序执行的结果是一致的。如下：<br>int a = 10;    //语句1<br>int r = 2;    //语句2<br>a = a + 3;    //语句3<br>r = a*a;     //语句4<br>则因为重排序，他还可能执行顺序为 2-1-3-4，1-3-2-4<br>但绝不可能 2-1-4-3，因为这打破了依赖关系。<br>显然重排序对单线程运行是不会有任何问题，而多线程就不一定了，所以我们在多线程编程时就得考虑这个问题了。</p>\n</li>\n<li><h2 id=\"java-\">Java内存模型</h2>\n<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;共享内存模型指的就是Java内存模型(简称JMM)，JMM决定一个线程对共享变量的写入时,能对另一个线程可见。从抽象的角度来看，JMM定义了线程和主内存之间的抽象关系：线程之间的共享变量存储在主内存（main memory）中，每个线程都有一个私有的本地内存（local memory），本地内存中存储了该线程以读/写共享变量的副本。本地内存是JMM的一个抽象概念，并不真实存在。它涵盖了缓存，写缓冲区，寄存器以及其他的硬件和编译器优化。<br><img src=\"http://xyw-blog.oss-cn-qingdao.aliyuncs.com/xblog/article/20190201174048670_image-1549014048072.png?Expires=1864374049&amp;OSSAccessKeyId=LTAIgFRAJMcFAUPX&amp;Signature=dVl07GADzT1D4QPLx8nSKkGf19w%3D\" alt=\"file\">   </p>\n</li>\n</ul>\n<p>从上图来看，线程A与线程B之间如要通信的话，必须要经历下面2个步骤：  </p>\n<ol>\n<li>首先，线程A把本地内存A中更新过的共享变量刷新到主内存中去。  </li>\n<li>然后，线程B到主内存中去读取线程A之前已更新过的共享变量。 </li>\n</ol>\n<p>下面通过示意图来说明这两个步骤：   </p>\n<p><img src=\"http://xyw-blog.oss-cn-qingdao.aliyuncs.com/xblog/article/20190201174253694_image-1549014173158.png?Expires=1864374174&amp;OSSAccessKeyId=LTAIgFRAJMcFAUPX&amp;Signature=EedKTx4G7v9cOiL6nI%2F1%2FdpC8Jc%3D\" alt=\"file\"></p>\n<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;如上图所示，本地内存A和B有主内存中共享变量x的副本。假设初始时，这三个内存中的x值都为0。线程A在执行时，把更新后的x值（假设值为1）临时存放在自己的本地内存A中。当线程A和线程B需要通信时，线程A首先会把自己本地内存中修改后的x值刷新到主内存中，此时主内存中的x值变为了1。随后，线程B到主内存中去读取线程A更新后的x值，此时线程B的本地内存的x值也变为了1。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;从整体来看，这两个步骤实质上是线程A在向线程B发送消息，而且这个通信过程必须要经过主内存。JMM通过控制主内存与每个线程的本地内存之间的交互，来为java程序员提供内存可见性保证。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color=\'red\'>总结：什么是Java内存模型：java内存模型简称jmm，定义了一个线程对另一个线程可见。共享变量存放在主内存中，每个线程都有自己的本地内存，当多个线程同时访问一个数据的时候，可能本地内存没有及时刷新到主内存，所以就会发生线程安全问题。</font></p>\n<ul>\n<li><h2 id=\"volatile\">Volatile</h2>\n</li>\n<li>Volatile 关键字的作用是变量在多个线程之间可见。</li>\n</ul>\n<pre><code class=\"lang-java\">package com.xuyiwei.my_02;\n\n/**\n * Created by xuyiwei on 2019/2/1.\n */\nclass VolatileThread extends Thread{\n    public  boolean flag = true;\n    @Override\n    public void run() {\n        System.out.println(&quot;开始执行子线程....&quot;);\n        while (flag) {\n        }\n        System.out.println(&quot;线程停止&quot;);\n    }\n    public void setRuning(boolean flag) {\n        this.flag = flag;\n    }\n\n}\npublic class ThreadVolatile {\n    public static void main(String[] args) throws InterruptedException {\n        VolatileThread threadVolatileDemo = new VolatileThread();\n        threadVolatileDemo.start();\n        //这个语句很关键  只要线程加拉睡眠时间  则通知内存的时候会延迟 之后想通知给主内存 只有手动通知了\n        Thread.sleep(3000);\n        threadVolatileDemo.setRuning(false);\n        System.out.println(&quot;flag 已经设置成false&quot;);\n        Thread.sleep(1000);\n        System.out.println(threadVolatileDemo.flag);\n        /**\n         开始执行子线程....\n         flag 已经设置成false\n         false\n\n         */\n    }\n}\n</code></pre>\n<p>上面的情况主线程不会将false刷新到主内存，因此子线程也不会读到内存刷新的flag=false，因此子线程会进入死循环，不会进去线程停止的语句。</p>\n<ul>\n<li>Volatile非原子性</li>\n</ul>\n<pre><code class=\"lang-java\">package com.xuyiwei.my_02;\n\n/**\n * Created by xuyiwei on 2019/2/3.\n */\npublic class VolatileNoAutreThread extends Thread{\n//    public static Object o = new Object();\n    public volatile static Integer one = 1;\n\n    @Override\n    public void run() {\n//        synchronized (o){\n            for(int i=0;i&lt;1000;i++){\n                one++;\n                System.out.print(Thread.currentThread().getName()+&quot;:&quot;+one+&quot; &quot;);\n\n            }\n//         }\n    }\n\n    public static void main(String[] args) {\n        VolatileNoAutreThread[] volatileThreads = new VolatileNoAutreThread[10];\n        for(int i=0;i&lt;10;i++){\n            volatileThreads[i] = new VolatileNoAutreThread();\n        }\n        for(int i=0;i&lt;10;i++){\n            volatileThreads[i].start();\n        }\n        //最后输出的结果不是10001，因为多线程做数据处理会出现数据冲突 及时加上volatile做线程间可见性处理 也没法确保数据原子性。因此还是加同步最为稳妥，volatile只能保证可见性,不能保证原子性\n    }\n}\n</code></pre>\n<ul>\n<li><h2 id=\"volatile-synchronized-\">volatile与synchronized区别</h2>\n<p>仅靠volatile不能保证线程的安全性。（原子性）<br>①volatile轻量级，只能修饰变量。synchronized重量级，还可修饰方法<br>②volatile只能保证数据的可见性，不能用来同步，因为多个线程并发访问volatile修饰的变量不会阻塞。<br>synchronized不仅保证可见性，而且还保证原子性，因为，只有获得了锁的线程才能进入临界区，从而保证临界区中的所有语句都全部执行。多个线程争抢synchronized锁对象时，会出现阻塞。<br>线程安全性<br>线程安全性包括两个方面，①可见性。②原子性。<br>从上面自增的例子中可以看出：仅仅使用volatile并不能保证线程安全性。而synchronized则可实现线程的安全性。  </p>\n</li>\n<li><h2 id=\"threadlocal\">ThreadLocal</h2>\n<p>ThreadLocal提高一个线程的局部变量，访问某个线程拥有自己局部变量。<br>当使用ThreadLocal维护变量时，ThreadLocal为每个使用该变量的线程提供独立的变量副本，所以每一个线程都可以独立地改变自己的副本，而不会影响其它线程所对应的副本。<br>ThreadLocal的接口方法<br>ThreadLocal类接口很简单，只有4个方法，我们先来了解一下：<br>•    void set(Object value)设置当前线程的线程局部变量的值。<br>•    public Object get()该方法返回当前线程所对应的线程局部变量。<br>•    public void remove()将当前线程局部变量的值删除，目的是为了减少内存的占用，该方法是JDK 5.0新增的方法。需要指出的是，当线程结束后，对应该线程的局部变量将自动被垃圾回收，所以显式调用该方法清除线程的局部变量并不是必须的操作，但它可以加快内存回收的速度。<br>protected Object    initialValue()返回该线程局部变量的初始值，该方法是一个protected的方法，显然是为了让子类覆盖而设计的。这个方法是一个延迟调用方法，在线程第1次调用get()或set(Object)时才执行，并且仅执行1次。ThreadLocal中的缺省实现直接返回一个null。  </p>\n</li>\n</ul>\n<pre><code class=\"lang-java\">package com.xuyiwei.my_02;\n\n/**\n * Created by xuyiwei on 2019/2/3.\n */\nclass Res{\n    public static  ThreadLocal&lt;Integer&gt; threadLocal = new ThreadLocal&lt;Integer&gt;(){\n        @Override\n        protected Integer initialValue() {\n            return 0;\n        }\n    };\n}\npublic class ThreadLocalDemo extends Thread {\n    private Res res;\n    public Res getRes() {\n        return res;\n    }\n    public void setRes(Res res) {\n        this.res = res;\n    }\n\n    @Override\n    public void run() {\n        for(int i=0;i&lt;10;i++){\n            Res.threadLocal.set(i);\n        }\n        System.out.println( Res.threadLocal.get());\n    }\n\n    public static void main(String[] args) {\n        Res res = new Res();\n        ThreadLocalDemo demo = new ThreadLocalDemo();\n        demo.setRes(res);\n        ThreadLocalDemo demo1 = new ThreadLocalDemo();\n        demo1.setRes(res);\n        demo.start();\n        demo1.start();\n        /**\n         9\n         9\n         */\n    }\n\n}\n</code></pre>\n<p>对于每个线程而言ThreadLocal都是一个独立的副本，即使Thread是一个静态变量。改变自己的ThreadLocal并不会影响其他线程的ThreadLocal。</p>\n', '- ## 什么是线程安全\n- #### 为什么会有线程安全的问题\n&nbsp;&nbsp;&nbsp;&nbsp;当多个线程同时共享，同一个全局变量或静态变量，做写的操作时，可能会发生数据冲突问题，也就是线程安全问题。但是做读操作是不会发生数据冲突问题。\n\n案例:需求现在有100张火车票，有两个窗口同时抢火车票，请使用多线程模拟抢票效果。\n\n```java\npackage com.xuyiwei.my_02;\nclass TicketWindow implements Runnable{\n    private static int i = 100;\n    private static Object object = new Object();\n    @Override\n    public void run() {\n        while (TicketWindow.i>0){\n            //只要确保同一时间只有一个线程在操作数据即可\n            synchronized (object){\n                if(i-->0){\n                    System.out.print(Thread.currentThread().getName()+\"卖出一张车票，还剩\"+i+\"张      \");\n                }\n            }\n        }\n    }\n}\npublic class TicketDemo01 {\n    public static void main(String[] args) {\n        TicketWindow window = new TicketWindow();\n        for(int z=0;z<10;z++){\n            Thread t = new Thread(window);\n            t.start();\n        }\n    }\n    /**\n     * Thread-0卖出一张车票，还剩99张      Thread-0卖出一张车票，还剩98张      Thread-0卖出一张车票，还剩97张      Thread-0卖出一张车票，还剩96张      Thread-0卖出一张车票，还剩95张      Thread-0卖出一张车票，还剩94张      Thread-0卖出一张车票，还剩93张      Thread-0卖出一张车票，还剩92张      Thread-0卖出一张车票，还剩91张      Thread-0卖出一张车票，还剩90张      Thread-0卖出一张车票，还剩89张      Thread-0卖出一张车票，还剩88张      Thread-0卖出一张车票，还剩87张      Thread-0卖出一张车票，还剩86张      Thread-0卖出一张车票，还剩85张      Thread-0卖出一张车票，还剩84张      Thread-0卖出一张车票，还剩83张      Thread-0卖出一张车票，还剩82张      Thread-0卖出一张车票，还剩81张      Thread-0卖出一张车票，还剩80张      Thread-0卖出一张车票，还剩79张      Thread-0卖出一张车票，还剩78张      Thread-0卖出一张车票，还剩77张      Thread-0卖出一张车票，还剩76张      Thread-0卖出一张车票，还剩75张      Thread-0卖出一张车票，还剩74张      Thread-0卖出一张车票，还剩73张      Thread-0卖出一张车票，还剩72张      Thread-0卖出一张车票，还剩71张      Thread-0卖出一张车票，还剩70张      Thread-0卖出一张车票，还剩69张      Thread-0卖出一张车票，还剩68张      Thread-0卖出一张车票，还剩67张      Thread-0卖出一张车票，还剩66张      Thread-0卖出一张车票，还剩65张      Thread-0卖出一张车票，还剩64张      Thread-0卖出一张车票，还剩63张      Thread-0卖出一张车票，还剩62张      Thread-0卖出一张车票，还剩61张      Thread-0卖出一张车票，还剩60张      Thread-0卖出一张车票，还剩59张      Thread-0卖出一张车票，还剩58张      Thread-0卖出一张车票，还剩57张      Thread-0卖出一张车票，还剩56张      Thread-0卖出一张车票，还剩55张      Thread-0卖出一张车票，还剩54张      Thread-0卖出一张车票，还剩53张      Thread-0卖出一张车票，还剩52张      Thread-0卖出一张车票，还剩51张      Thread-0卖出一张车票，还剩50张      Thread-0卖出一张车票，还剩49张      Thread-0卖出一张车票，还剩48张      Thread-0卖出一张车票，还剩47张      Thread-0卖出一张车票，还剩46张      Thread-0卖出一张车票，还剩45张      Thread-0卖出一张车票，还剩44张      Thread-0卖出一张车票，还剩43张      Thread-0卖出一张车票，还剩42张      Thread-0卖出一张车票，还剩41张      Thread-0卖出一张车票，还剩40张      Thread-0卖出一张车票，还剩39张      Thread-0卖出一张车票，还剩38张      Thread-0卖出一张车票，还剩37张      Thread-0卖出一张车票，还剩36张      Thread-0卖出一张车票，还剩35张      Thread-0卖出一张车票，还剩34张      Thread-0卖出一张车票，还剩33张      Thread-0卖出一张车票，还剩32张      Thread-0卖出一张车票，还剩31张      Thread-1卖出一张车票，还剩30张      Thread-1卖出一张车票，还剩29张      Thread-1卖出一张车票，还剩28张      Thread-1卖出一张车票，还剩27张      Thread-1卖出一张车票，还剩26张      Thread-1卖出一张车票，还剩25张      Thread-1卖出一张车票，还剩24张      Thread-1卖出一张车票，还剩23张      Thread-1卖出一张车票，还剩22张      Thread-1卖出一张车票，还剩21张      Thread-1卖出一张车票，还剩20张      Thread-1卖出一张车票，还剩19张      Thread-1卖出一张车票，还剩18张      Thread-1卖出一张车票，还剩17张      Thread-1卖出一张车票，还剩16张      Thread-1卖出一张车票，还剩15张      Thread-1卖出一张车票，还剩14张      Thread-1卖出一张车票，还剩13张      Thread-1卖出一张车票，还剩12张      Thread-1卖出一张车票，还剩11张      Thread-1卖出一张车票，还剩10张      Thread-1卖出一张车票，还剩9张      Thread-1卖出一张车票，还剩8张      Thread-1卖出一张车票，还剩7张      Thread-1卖出一张车票，还剩6张      Thread-1卖出一张车票，还剩5张      Thread-1卖出一张车票，还剩4张      Thread-1卖出一张车票，还剩3张      Thread-1卖出一张车票，还剩2张      Thread-1卖出一张车票，还剩1张      Thread-1卖出一张车票，还剩0张      \n     */\n}\n```\n\n如果不加synchronized，会导致两个窗口同时卖同一张车票，容易出现发生数据冲突。\n\n- ## 线程安全的解决办法\n\n<font color=\'red\'>问:如何解决多线程之间线程安全问题?<br></font>\n答:使用多线程之间同步synchronized或使用锁(lock)。<br> \n\n<font color=\'red\'>问:为什么使用线程同步或使用锁能解决线程安全问题呢？<br></font>\n答:将可能会发生数据冲突问题(线程不安全问题)，只能让当前一个线程进行执行。代码执行完成后释放锁，让后才能让其他线程进行执行。这样的话就可以解决线程不安全问题。<br> \n\n<font color=\'red\'>问:什么是多线程之间同步？<br></font> \n<font >答:当多个线程共享同一个资源,不会受到其他线程的干扰。</font>\n\n- ## Lock锁\n首先要说明的就是Lock，通过查看Lock的源码可知，Lock是一个接口：\n\n```java\npublic interface Lock {\n    void lock();\n    void lockInterruptibly() throws InterruptedException;\n    boolean tryLock();\n    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;\n    void unlock();\n    Condition newCondition();\n}\n```\n　下面来逐个讲述Lock接口中每个方法的使用，lock()、tryLock()、tryLock(long time, TimeUnit unit)和lockInterruptibly()是用来获取锁的。unLock()方法是用来释放锁的。newCondition()这个方法暂且不在此讲述，会在后面的线程协作一文中讲述。\n\n　　在Lock中声明了四个方法来获取锁，那么这四个方法有何区别呢？\n\n　　首先lock()方法是平常使用得最多的一个方法，就是用来获取锁。如果锁已被其他线程获取，则进行等待。\n\n　　由于在前面讲到如果采用Lock，必须主动去释放锁，并且在发生异常时，不会自动释放锁。因此一般来说，使用Lock必须在try{}catch{}块中进行，并且将释放锁的操作放在finally块中进行，以保证锁一定被被释放，防止死锁的发生。通常使用Lock来进行同步的话，是以下面这种形式去使用的：\n\n```java\nLock lock = ...;\nlock.lock();\ntry{\n    //处理任务\n}catch(Exception ex){\n     \n}finally{\n    lock.unlock();   //释放锁\n}\n```\n　tryLock()方法是有返回值的，它表示用来尝试获取锁，如果获取成功，则返回true，如果获取失败（即锁已被其他线程获取），则返回false，也就说这个方法无论如何都会立即返回。在拿不到锁时不会一直在那等待。\n\n　　tryLock(long time, TimeUnit unit)方法和tryLock()方法是类似的，只不过区别在于这个方法在拿不到锁时会等待一定的时间，在时间期限之内如果还拿不到锁，就返回false。如果如果一开始拿到锁或者在等待期间内拿到了锁，则返回true。\n\n　　所以，一般情况下通过tryLock来获取锁时是这样使用的：\n\n```java\nLock lock = ...;\nif(lock.tryLock()) {\n     try{\n         //处理任务\n     }catch(Exception ex){\n         \n     }finally{\n         lock.unlock();   //释放锁\n     } \n}else {\n    //如果不能获取锁，则直接做其他事情\n}\n```\n\n　lockInterruptibly()方法比较特殊，当通过这个方法去获取锁时，如果线程正在等待获取锁，则这个线程能够响应中断，即中断线程的等待状态。也就使说，当两个线程同时通过lock.lockInterruptibly()想获取某个锁时，假若此时线程A获取到了锁，而线程B只有在等待，那么对线程B调用threadB.interrupt()方法能够中断线程B的等待过程。\n\n　　由于lockInterruptibly()的声明中抛出了异常，所以lock.lockInterruptibly()必须放在try块中或者在调用lockInterruptibly()的方法外声明抛出InterruptedException。\n\n　　因此lockInterruptibly()一般的使用形式如下：\n　　\n```java\npackage com.xuyiwei.my_02;\n\n/**\n * Created by xuyiwei on 2019/2/14.\n */\nimport java.util.concurrent.locks.Lock;\nimport java.util.concurrent.locks.ReentrantLock;\n\nclass BussinessClass {\n\n    private Lock lock = new ReentrantLock();\n\n    // 业务方法\n    public void bFuction() {\n        String tName = Thread.currentThread().getName();\n        try {\n            System.out.println(tName + \"-开始获取锁..........\");\n            //这种方式获取的锁是可以被中断的  lock相对于synchronized有这个好处 但是lock锁住的都是对象本身\n            lock.lockInterruptibly();\n\n            System.out.println(tName + \"-获取到锁了！！！！\");\n            System.out.println(tName + \"-睡觉了，睡个30秒！\");\n            Thread.sleep(30000);\n            System.out.println(tName + \"-睡醒了，干活！\");\n            for (int i = 0; i < 5; i++) {\n                System.out.println(tName + \"：\" + i);\n            }\n        } catch (Exception e) {\n            System.out.println(tName + \"-我好像被中断了！\");\n            e.printStackTrace();\n        } finally {\n            lock.unlock();\n            System.out.println(tName + \"-释放了锁\");\n        }\n    }\n}\npublic class LockTest3 {\n    public static void main(String[] args) {\n        BussinessClass bc = new BussinessClass();\n        Thread t1 = new Thread(new Runnable() {\n            @Override\n            public void run() {\n                bc.bFuction();\n            }\n        });\n        Thread t2 = new Thread(new Runnable() {\n            @Override\n            public void run() {\n                bc.bFuction();\n            }\n        });\n        t1.start();\n        t2.start();\n        //这里可以中断lockInterruptibly加锁的线程\n        t1.interrupt();\n        /**\n         * Thread-0-开始获取锁..........\n         Thread-1-开始获取锁..........\n         Thread-0-获取到锁了！！！！\n         Thread-0-睡觉了，睡个30秒！\n         Thread-0-我好像被中断了！\n         Thread-0-释放了锁\n         Thread-1-获取到锁了！！！！\n         Thread-1-睡觉了，睡个30秒！\n         java.lang.InterruptedException: sleep interrupted\n         at java.lang.Thread.sleep(Native Method)\n         at com.xuyiwei.my_02.BussinessClass.bFuction(LockTest3.java:26)\n         at com.xuyiwei.my_02.LockTest3$1.run(LockTest3.java:46)\n         at java.lang.Thread.run(Thread.java:744)\n         Thread-1-睡醒了，干活！\n         Thread-1：0\n         Thread-1：1\n         Thread-1：2\n         Thread-1：3\n         Thread-1：4\n         Thread-1-释放了锁\n\n         */\n    }\n}\n\n```\n\n　注意，当一个线程获取了锁之后，是不会被interrupt()方法中断的。因为本身在前面的文章中讲过单独调用interrupt()方法不能中断正在运行过程中的线程，只能中断阻塞过程中的线程。\n\n　　因此当通过lockInterruptibly()方法获取某个锁时，如果不能获取到，只有进行等待的情况下，是可以响应中断的。\n\n　　而用synchronized修饰的话，当一个线程处于等待某个锁的状态，是无法被中断的，只有一直等待下去。\n\n- ## 同步代码块\n\n什么是同步代码块？<br>\n答:就是将可能会发生线程安全问题的代码，给包括起来。<br>\nsynchronized(同一个数据){<br>\n    可能会发生线程冲突问题<br>\n}<br>\n就是同步代码块 <br>\nsynchronized(对象)//这个对象可以为任意对象 <br>\n{ <br>\n    需要被同步的代码 <br>\n} <br>\n <br>\n对象如同锁，持有锁的线程可以在同步中执行 <br>\n没持有锁的线程即使获取CPU的执行权，也进不去 <br>\n同步的前提： <br>\n1，必须要有两个或者两个以上的线程 <br>\n2，必须是多个线程使用同一个锁 <br>\n必须保证同步中只能有一个线程在运行 <br>\n好处：解决了多线程的安全问题 <br>\n弊端：多个线程需要判断锁，较为消耗资源、抢锁的资源。 <br>\n\n\n```java\npackage com.xuyiwei.my_02;\n\n/**\n * Created by xuyiwei on 2019/1/30.\n */\npublic class SynchronizedDemo implements Runnable{\n    private static Object obj = new Object();\n    @Override\n    public void run() {\n        synchronized (SynchronizedDemo.obj){\n            for(int i=0;i<50;i++){\n                System.out.print(Thread.currentThread().getName()+\":\"+i+\"  \");\n            }\n        }\n    }\n\n    public static void main(String[] args) {\n        SynchronizedDemo synchronizedDemo = new SynchronizedDemo();\n        Thread thread1 = new Thread(synchronizedDemo);\n        Thread thread2 = new Thread(synchronizedDemo);\n        thread1.start();\n        thread2.start();\n        /**\n         Thread-0:0  Thread-0:1  Thread-0:2  Thread-0:3  Thread-0:4  Thread-0:5  Thread-0:6  Thread-0:7  Thread-0:8  Thread-0:9  Thread-0:10  Thread-0:11  Thread-0:12  Thread-0:13  Thread-0:14  Thread-0:15  Thread-0:16  Thread-0:17  Thread-0:18  Thread-0:19  Thread-0:20  Thread-0:21  Thread-0:22  Thread-0:23  Thread-0:24  Thread-0:25  Thread-0:26  Thread-0:27  Thread-0:28  Thread-0:29  Thread-0:30  Thread-0:31  Thread-0:32  Thread-0:33  Thread-0:34  Thread-0:35  Thread-0:36  Thread-0:37  Thread-0:38  Thread-0:39  Thread-0:40  Thread-0:41  Thread-0:42  Thread-0:43  Thread-0:44  Thread-0:45  Thread-0:46  Thread-0:47  Thread-0:48  Thread-0:49  Thread-1:0  Thread-1:1  Thread-1:2  Thread-1:3  Thread-1:4  Thread-1:5  Thread-1:6  Thread-1:7  Thread-1:8  Thread-1:9  Thread-1:10  Thread-1:11  Thread-1:12  Thread-1:13  Thread-1:14  Thread-1:15  Thread-1:16  Thread-1:17  Thread-1:18  Thread-1:19  Thread-1:20  Thread-1:21  Thread-1:22  Thread-1:23  Thread-1:24  Thread-1:25  Thread-1:26  Thread-1:27  Thread-1:28  Thread-1:29  Thread-1:30  Thread-1:31  Thread-1:32  Thread-1:33  Thread-1:34  Thread-1:35  Thread-1:36  Thread-1:37  Thread-1:38  Thread-1:39  Thread-1:40  Thread-1:41  Thread-1:42  Thread-1:43  Thread-1:44  Thread-1:45  Thread-1:46  Thread-1:47  Thread-1:48  Thread-1:49\n         */\n    }\n}\n```\n\n- ## 同步函数\n什么是同步函数？<br>\n答：在方法上修饰synchronized 称为同步函数<br>\n\n同步函数本质上使用了this锁\n\n```java\npackage com.xuyiwei.my_02;\n\n/**\n * Created by xuyiwei on 2019/1/30.\n */\npublic class SynchronizedMethod implements Runnable {\n    private boolean flag = true;\n    @Override\n    public void run() {\n        if(flag){\n            synchronized (this){\n                for(int i=0;i<20;i++){\n                    System.out.print(Thread.currentThread().getName()+\":\"+i+\" \");\n                }\n                System.out.println();\n            }\n        }else{\n            sale();\n        }\n    }\n\n    public synchronized void sale(){\n        for(int i=0;i<20;i++){\n            System.out.print(Thread.currentThread().getName()+\":\"+i+\" \");\n        }\n        System.out.println();\n    }\n\n\n    public boolean isFlag() {\n        return flag;\n    }\n\n    public void setFlag(boolean flag) {\n        this.flag = flag;\n    }\n\n    public static void main(String[] args) {\n        SynchronizedMethod synchronizedMethod = new SynchronizedMethod();\n        Thread thread1 = new Thread(synchronizedMethod,\"1号线程\");\n        Thread thread2 = new Thread(synchronizedMethod,\"2号线程\");\n        thread1.start();\n        synchronizedMethod.setFlag(false);\n        thread2.start();\n        /**\n         1号线程:0 1号线程:1 1号线程:2 1号线程:3 1号线程:4 1号线程:5 1号线程:6 1号线程:7 1号线程:8 1号线程:9 1号线程:10 1号线程:11 1号线程:12 1号线程:13 1号线程:14 1号线程:15 1号线程:16 1号线程:17 1号线程:18 1号线程:19\n         2号线程:0 2号线程:1 2号线程:2 2号线程:3 2号线程:4 2号线程:5 2号线程:6 2号线程:7 2号线程:8 2号线程:9 2号线程:10 2号线程:11 2号线程:12 2号线程:13 2号线程:14 2号线程:15 2号线程:16 2号线程:17 2号线程:18 2号线程:19           证明主线程和子线程占用的是同一个锁，因为没有起冲突\n         */\n    }\n}\n\n```\n\n- ## 静态同步函数\n答：什么是静态同步函数？<br>\n方法上加上static关键字，使用synchronized 关键字修饰 或者使用类.class文件。<br>\n静态的同步函数使用的锁是  该函数所属字节码文件对象 \n可以用 getClass方法获取，也可以用当前  类名.class 表示。\n\n```java\npackage com.xuyiwei.my_02;\n\n/**\n * Created by xuyiwei on 2019/1/30.\n */\npublic class ClassSynchronized implements Runnable{\n    int trainCount = 100;\n    @Override\n    public void run() {\n        synchronized (ClassSynchronized.class) {\n            System.out.println(Thread.currentThread().getName() + \",出售 第\" + (100 - trainCount + 1) + \"张票.\");\n            trainCount--;\n            try {\n                Thread.sleep(100);\n            } catch (Exception e) {\n            }\n        }\n    }\n}\n\n```\n\n- ## 多线程死锁\n- #### 什么是多线程死锁？\n同步中嵌套同步,导致锁无法释放\n\n```java\npackage com.xuyiwei.my_02;\n\n/**\n * Created by xuyiwei on 2019/1/30.\n */\npublic class DeadLocked implements Runnable {\n    private static Object obj = new Object();\n    public boolean flag = true;\n    @Override\n    public void run() {\n        if(flag){\n            /**\n             * 这里死锁的原因 1号线程需要执行，先需要获得obj锁，然后在获得this锁\n             * 2号线程执行，需要先获得this锁，然后获得obj锁\n             * 当1号线程占据obj锁后进入睡眠，2号线程占据this锁之后进入睡眠，但是两个线程都需要去等对方释放锁才能进行下一步\n             * 因此变成了死锁\n             */\n            synchronized (obj){\n                try {\n                    Thread.sleep(1000);\n                } catch (InterruptedException e) {\n                    e.printStackTrace();\n                }\n\n                synchronized(this){\n                    for(int i=0;i<10000;i++){\n                        System.out.print(\"run:\"+i+\"  \");\n                    }\n                }\n            }\n        }else{\n            try {\n                sale();\n            } catch (InterruptedException e) {\n                e.printStackTrace();\n            }\n        }\n\n    }\n\n    public synchronized void sale() throws InterruptedException {\n        synchronized (obj){\n            Thread.sleep(40);\n\n            for(int i=0;i<1000;i++){\n                System.out.print(\"sale:\"+i+\"  \");\n            }\n        }\n    }\n\n    public static void main(String[] args) throws InterruptedException {\n        DeadLocked deadLocked = new DeadLocked();\n        Thread thread = new Thread(deadLocked);\n        Thread thread1 = new Thread(deadLocked);\n        thread.start();\n        //这里必须要睡眠 因为 deadLocked.flag设置成false的时候 有可能线程还没进入true的方法  然后1号死锁线程也需要睡眠，确保二号线程开启之前 她已经占据锁了 这样才能模拟死锁\n        Thread.sleep(40);\n        deadLocked.flag = false;\n        thread1.start();\n    }\n}\n\n```\n\n- ## 多线程的三大特性\n原子性、可见性、有序性\n- #### 什么是原子性\n即一个操作或者多个操作        \n要么全部执行并且执行的过程不会被任何因素打断，要么就都不执行。   \n一个很经典的例子就是银行账户转账问题：    \n比如从账户A向账户B转1000元，那么必然包括2个操作：从账户A减去1000元，往账户B加上1000元。这2个操作必须要具备原子性才能保证不出现一些意外的问题。  \n我们操作数据也是如此，比如i=i+1；其中就包括，读取i的值，计算i，写入i。这行代码在Java中是不具备原子性的，则多线程运行肯定会出问题，所以也需要我们使用同步和lock这些东西来确保这个特性了。  \n原子性其实就是保证数据一致、线程安全一部分。\n\n- #### 什么是可见性\n当多个线程访问同一个变量时，一个线程修改了这个变量的值，其他线程能够立即看得到修改的值。  \n若两个线程在不同的cpu，那么线程1改变了i的值还没刷新到主存，线程2又使用了i，那么这个i值肯定还是之前的，线程1对变量的修改线程没看到这就是可见性问题。 \n\n- #### 什么是有序性\n程序执行的顺序按照代码的先后顺序执行。  \n一般来说处理器为了提高程序运行效率，可能会对输入代码进行优化，它不保证程序中各个语句的执行先后顺序同代码中的顺序一致，但是它会保证程序最终执行结果和代码顺序执行的结果是一致的。如下：  \nint a = 10;    //语句1  \nint r = 2;    //语句2  \na = a + 3;    //语句3  \nr = a*a;     //语句4  \n则因为重排序，他还可能执行顺序为 2-1-3-4，1-3-2-4\n但绝不可能 2-1-4-3，因为这打破了依赖关系。\n显然重排序对单线程运行是不会有任何问题，而多线程就不一定了，所以我们在多线程编程时就得考虑这个问题了。\n\n- ## Java内存模型\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;共享内存模型指的就是Java内存模型(简称JMM)，JMM决定一个线程对共享变量的写入时,能对另一个线程可见。从抽象的角度来看，JMM定义了线程和主内存之间的抽象关系：线程之间的共享变量存储在主内存（main memory）中，每个线程都有一个私有的本地内存（local memory），本地内存中存储了该线程以读/写共享变量的副本。本地内存是JMM的一个抽象概念，并不真实存在。它涵盖了缓存，写缓冲区，寄存器以及其他的硬件和编译器优化。\n![file](http://xyw-blog.oss-cn-qingdao.aliyuncs.com/xblog/article/20190201174048670_image-1549014048072.png?Expires=1864374049&OSSAccessKeyId=LTAIgFRAJMcFAUPX&Signature=dVl07GADzT1D4QPLx8nSKkGf19w%3D)   \n\n\n\n从上图来看，线程A与线程B之间如要通信的话，必须要经历下面2个步骤：  \n1. 首先，线程A把本地内存A中更新过的共享变量刷新到主内存中去。  \n2. 然后，线程B到主内存中去读取线程A之前已更新过的共享变量。 \n\n下面通过示意图来说明这两个步骤：   \n\n![file](http://xyw-blog.oss-cn-qingdao.aliyuncs.com/xblog/article/20190201174253694_image-1549014173158.png?Expires=1864374174&OSSAccessKeyId=LTAIgFRAJMcFAUPX&Signature=EedKTx4G7v9cOiL6nI%2F1%2FdpC8Jc%3D)\n\n\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;如上图所示，本地内存A和B有主内存中共享变量x的副本。假设初始时，这三个内存中的x值都为0。线程A在执行时，把更新后的x值（假设值为1）临时存放在自己的本地内存A中。当线程A和线程B需要通信时，线程A首先会把自己本地内存中修改后的x值刷新到主内存中，此时主内存中的x值变为了1。随后，线程B到主内存中去读取线程A更新后的x值，此时线程B的本地内存的x值也变为了1。  \n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;从整体来看，这两个步骤实质上是线程A在向线程B发送消息，而且这个通信过程必须要经过主内存。JMM通过控制主内存与每个线程的本地内存之间的交互，来为java程序员提供内存可见性保证。  \n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color=\'red\'>总结：什么是Java内存模型：java内存模型简称jmm，定义了一个线程对另一个线程可见。共享变量存放在主内存中，每个线程都有自己的本地内存，当多个线程同时访问一个数据的时候，可能本地内存没有及时刷新到主内存，所以就会发生线程安全问题。</font>\n\n\n- ## Volatile\n- Volatile 关键字的作用是变量在多个线程之间可见。\n\n```java\npackage com.xuyiwei.my_02;\n\n/**\n * Created by xuyiwei on 2019/2/1.\n */\nclass VolatileThread extends Thread{\n    public  boolean flag = true;\n    @Override\n    public void run() {\n        System.out.println(\"开始执行子线程....\");\n        while (flag) {\n        }\n        System.out.println(\"线程停止\");\n    }\n    public void setRuning(boolean flag) {\n        this.flag = flag;\n    }\n\n}\npublic class ThreadVolatile {\n    public static void main(String[] args) throws InterruptedException {\n        VolatileThread threadVolatileDemo = new VolatileThread();\n        threadVolatileDemo.start();\n        //这个语句很关键  只要线程加拉睡眠时间  则通知内存的时候会延迟 之后想通知给主内存 只有手动通知了\n        Thread.sleep(3000);\n        threadVolatileDemo.setRuning(false);\n        System.out.println(\"flag 已经设置成false\");\n        Thread.sleep(1000);\n        System.out.println(threadVolatileDemo.flag);\n        /**\n         开始执行子线程....\n         flag 已经设置成false\n         false\n        \n         */\n    }\n}\n\n```\n\n上面的情况主线程不会将false刷新到主内存，因此子线程也不会读到内存刷新的flag=false，因此子线程会进入死循环，不会进去线程停止的语句。\n\n- Volatile非原子性\n\n```java\npackage com.xuyiwei.my_02;\n\n/**\n * Created by xuyiwei on 2019/2/3.\n */\npublic class VolatileNoAutreThread extends Thread{\n//    public static Object o = new Object();\n    public volatile static Integer one = 1;\n\n    @Override\n    public void run() {\n//        synchronized (o){\n            for(int i=0;i<1000;i++){\n                one++;\n                System.out.print(Thread.currentThread().getName()+\":\"+one+\" \");\n\n            }\n//         }\n    }\n\n    public static void main(String[] args) {\n        VolatileNoAutreThread[] volatileThreads = new VolatileNoAutreThread[10];\n        for(int i=0;i<10;i++){\n            volatileThreads[i] = new VolatileNoAutreThread();\n        }\n        for(int i=0;i<10;i++){\n            volatileThreads[i].start();\n        }\n        //最后输出的结果不是10001，因为多线程做数据处理会出现数据冲突 及时加上volatile做线程间可见性处理 也没法确保数据原子性。因此还是加同步最为稳妥，volatile只能保证可见性,不能保证原子性\n    }\n}\n\n\n\n```\n\n- ## volatile与synchronized区别\n仅靠volatile不能保证线程的安全性。（原子性）    \n①volatile轻量级，只能修饰变量。synchronized重量级，还可修饰方法  \n②volatile只能保证数据的可见性，不能用来同步，因为多个线程并发访问volatile修饰的变量不会阻塞。   \nsynchronized不仅保证可见性，而且还保证原子性，因为，只有获得了锁的线程才能进入临界区，从而保证临界区中的所有语句都全部执行。多个线程争抢synchronized锁对象时，会出现阻塞。  \n线程安全性  \n线程安全性包括两个方面，①可见性。②原子性。  \n从上面自增的例子中可以看出：仅仅使用volatile并不能保证线程安全性。而synchronized则可实现线程的安全性。  \n\n- ## ThreadLocal  \n ThreadLocal提高一个线程的局部变量，访问某个线程拥有自己局部变量。  \n 当使用ThreadLocal维护变量时，ThreadLocal为每个使用该变量的线程提供独立的变量副本，所以每一个线程都可以独立地改变自己的副本，而不会影响其它线程所对应的副本。  \nThreadLocal的接口方法 \nThreadLocal类接口很简单，只有4个方法，我们先来了解一下：  \n•	void set(Object value)设置当前线程的线程局部变量的值。  \n•	public Object get()该方法返回当前线程所对应的线程局部变量。  \n•	public void remove()将当前线程局部变量的值删除，目的是为了减少内存的占用，该方法是JDK 5.0新增的方法。需要指出的是，当线程结束后，对应该线程的局部变量将自动被垃圾回收，所以显式调用该方法清除线程的局部变量并不是必须的操作，但它可以加快内存回收的速度。  \nprotected Object    initialValue()返回该线程局部变量的初始值，该方法是一个protected的方法，显然是为了让子类覆盖而设计的。这个方法是一个延迟调用方法，在线程第1次调用get()或set(Object)时才执行，并且仅执行1次。ThreadLocal中的缺省实现直接返回一个null。  \n\n```java\npackage com.xuyiwei.my_02;\n\n/**\n * Created by xuyiwei on 2019/2/3.\n */\nclass Res{\n    public static  ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>(){\n        @Override\n        protected Integer initialValue() {\n            return 0;\n        }\n    };\n}\npublic class ThreadLocalDemo extends Thread {\n    private Res res;\n    public Res getRes() {\n        return res;\n    }\n    public void setRes(Res res) {\n        this.res = res;\n    }\n\n    @Override\n    public void run() {\n        for(int i=0;i<10;i++){\n            Res.threadLocal.set(i);\n        }\n        System.out.println( Res.threadLocal.get());\n    }\n\n    public static void main(String[] args) {\n        Res res = new Res();\n        ThreadLocalDemo demo = new ThreadLocalDemo();\n        demo.setRes(res);\n        ThreadLocalDemo demo1 = new ThreadLocalDemo();\n        demo1.setRes(res);\n        demo.start();\n        demo1.start();\n        /**\n         9\n         9\n         */\n    }\n\n}\n```\n对于每个线程而言ThreadLocal都是一个独立的副本，即使Thread是一个静态变量。改变自己的ThreadLocal并不会影响其他线程的ThreadLocal。', b'0', '2', '1', b'0', b'0', 'java多线程安全入门', 'java 多线程 安全 ', b'1', '2019-02-03 07:14:30', '2019-02-03 07:14:30');
INSERT INTO `biz_article` VALUES ('4', '2019-02-12随笔', '1', 'xblog/cover/20190213133014515_b79a2572eccb402a863cadce51ee20bc.jpg', null, b'1', '<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;今天是假期的最后一天，照例天下着下雨，心情不是很美好，每次回杭州的时候，心情总会很低落，有点心酸，我不是一个适合离家很远的人。本来今天想出去走走的，最后一天了，但是妈妈叫了外婆他们来家里吃饭，下午跟妈妈发火了，因为整理衣服的问题，现在想想，好没必要，我想家了。在杭州的每一天都想回家。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;出门的时候，妈妈说记得到杭州了打电话给她，我开玩笑的说不用打的，不会走丢。现在也不知道那时候哪来的勇气，自己一个人拉着行李箱来的杭州。最后还是给妈妈打了，带了点哭腔，我感觉妈妈也带了点。然后我就把电话挂了。从地铁站走回家的路上没带雨伞，然后还傻乎乎的走反了。到家之后，买了乐刻的体验卡，跑了40分钟的步，有点迷茫，也有点无助。希望明天天会晴朗。</p>\n', '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;今天是假期的最后一天，照例天下着下雨，心情不是很美好，每次回杭州的时候，心情总会很低落，有点心酸，我不是一个适合离家很远的人。本来今天想出去走走的，最后一天了，但是妈妈叫了外婆他们来家里吃饭，下午跟妈妈发火了，因为整理衣服的问题，现在想想，好没必要，我想家了。在杭州的每一天都想回家。\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;出门的时候，妈妈说记得到杭州了打电话给她，我开玩笑的说不用打的，不会走丢。现在也不知道那时候哪来的勇气，自己一个人拉着行李箱来的杭州。最后还是给妈妈打了，带了点哭腔，我感觉妈妈也带了点。然后我就把电话挂了。从地铁站走回家的路上没带雨伞，然后还傻乎乎的走反了。到家之后，买了乐刻的体验卡，跑了40分钟的步，有点迷茫，也有点无助。希望明天天会晴朗。', b'0', '3', '1', b'0', b'1', '2019-02-12日记', '2019-02-12日记', b'1', '2019-02-13 05:30:33', '2019-02-13 05:30:33'), ('5', 'java并发包&线程池原理分析&锁的深度化', '1', 'xblog/cover/20190214113327099_20190203151358162_timg.jpeg', null, b'1', '<ul>\n<li><h2 id=\"-\">同步容器类</h2>\n</li>\n<li>Vector与ArrayList区别 </li>\n</ul>\n<p>1.ArrayList是最常用的List实现类，内部是通过数组实现的，它允许对元素进行快速随机访问。数组的缺点是每个元素之间不能有间隔，当数组大小不满足时需要增加存储能力，就要讲已经有数组的数据复制到新的存储空间中。当从ArrayList的中间位置插入或者删除元素时，需要对数组进行复制、移动、代价比较高。因此，它适合随机查找和遍历，不适合插入和删除。<br>2.Vector与ArrayList一样，也是通过数组实现的，不同的是它支持线程的同步，即某一时刻只有一个线程能够写Vector，避免多线程同时写而引起的不一致性，但实现同步需要很高的花费，因此，访问它比访问ArrayList慢<br>注意: Vector线程安全、ArrayList  </p>\n<pre><code class=\"lang-java\">package com.xuyiwei.my_03;\n\nimport java.util.ArrayList;\nimport java.util.Vector;\n\n/**\n * Created by xuyiwei on 2019/2/3.\n */\npublic class ListDemo extends Thread{\n    ArrayList&lt;Integer&gt; arrayList;\n    Vector&lt;Integer&gt; vector;\n    public ListDemo(ArrayList&lt;Integer&gt; arrayList, Vector&lt;Integer&gt; vector) {\n        this.arrayList = arrayList;\n        this.vector = vector;\n    }\n    @Override\n    public void run() {\n        for(int i=0;i&lt;20;i++){\n            arrayList.add(i );\n            vector.add(i );\n        }\n    }\n\n    public static void main(String[] args) {\n        ArrayList&lt;Integer&gt; list = new ArrayList&lt;&gt;();\n        Vector&lt;Integer&gt; vector = new Vector&lt;&gt;();\n        ListDemo demo = new ListDemo(list,vector);\n        ListDemo demo1 = new ListDemo(list,vector);\n        demo.start();\n        demo1.start();\n        try {\n            Thread.sleep(3000);\n        } catch (InterruptedException e) {\n            e.printStackTrace();\n        }\n        //因为list线程不安全  因此会导致有些值add不进去 甚至报错现象 因为arrayList本质还是数组  数组下标多个线程同时操作会出问题\n        System.out.println(&quot;listSize:&quot;+list.size()+&quot; &quot;);\n        for(Integer i :list){\n            System.out.print(&quot;list:&quot;+i+&quot; &quot;);\n        }\n        System.out.println(&quot; &quot;);\n        //因为vector线程安全 所以所有值都能add进去 就是20个\n        System.out.println(&quot;vectorSize:&quot;+vector.size()+&quot; &quot;);\n\n        for(Integer i :vector){\n            System.out.print(&quot;vector:&quot;+i+&quot; &quot;);\n        }\n\n        /**\n         listSize:39\n         list:0 list:0 list:1 list:2 list:1 list:3 list:2 list:4 list:3 list:5 list:4 list:5 list:7 list:6 list:8 list:null list:9 list:8 list:10 list:9 list:11 list:10 list:null list:11 list:13 list:12 list:14 list:13 list:15 list:14 list:16 list:15 list:17 list:null list:18 list:17 list:19 list:18 list:19\n         vectorSize:40\n         vector:0 vector:1 vector:0 vector:2 vector:1 vector:3 vector:2 vector:4 vector:5 vector:3 vector:4 vector:6 vector:5 vector:7 vector:6 vector:8 vector:7 vector:9 vector:8 vector:10 vector:9 vector:11 vector:10 vector:12 vector:11 vector:13 vector:12 vector:14 vector:13 vector:15 vector:14 vector:16 vector:15 vector:17 vector:16 vector:18 vector:17 vector:19 vector:18 vector:19\n         */\n    }\n}\n</code></pre>\n<ul>\n<li>Vector源码类 </li>\n</ul>\n<p>Add方法源码类<br><img src=\"http://xyw-blog.oss-cn-qingdao.aliyuncs.com/xblog/article/20190203155626103_image-1549180585637.png?Expires=1864540586&amp;OSSAccessKeyId=LTAIgFRAJMcFAUPX&amp;Signature=1hMjZlyLNgDkpe8vVuBE7VzcpGc%3D\" alt=\"file\"></p>\n<ul>\n<li>Arraylist源码  </li>\n</ul>\n<p>Add方法源码类<br><img src=\"http://xyw-blog.oss-cn-qingdao.aliyuncs.com/xblog/article/20190203155713080_image-1549180632866.png?Expires=1864540633&amp;OSSAccessKeyId=LTAIgFRAJMcFAUPX&amp;Signature=PeajLdiVbwSeVHtmvPLd2zvbAA8%3D\" alt=\"file\"></p>\n<ul>\n<li>HasTable与HasMap  </li>\n</ul>\n<p>1.HashMap不是线程安全的<br>HastMap是一个接口是map接口的子接口，是将键映射到值的对象，其中键和值都是对象，并且不能包含重复键，但可以包含重复值。HashMap允许null key和null value，而hashtable不允许。<br>2.HashTable是线程安全的一个Collection。<br>3.HashMap是Hashtable的轻量级实现（非线程安全的实现），他们都完成了Map接口，主要区别在于HashMap允许空（null）键值（key）,由于非线程安全，效率上可能高于Hashtable。HashMap允许将null作为一个entry的key或者value，而Hashtable不允许。HashMap把Hashtable的contains方法去掉了，改成containsvalue和containsKey。<br>注意: HashTable线程安全，HashMap线程不安全。<br>源码分析<br>其实仔细一想虽然HashTable线程安全，但是并没有多大用处，他只能保证HashTable执行单个方法的时候线程安全，但是没法确保一个方法里面都安全。</p>\n<pre><code class=\"lang-java\">  public void run() {\n        for(int i=0;i&lt;1000;i++){\n            if(!hashMap.containsKey(i+&quot; &quot;)){\n                hashMap.put(i+&quot; &quot;,i);\n            }\n            if(!hashtable.contains(i+&quot; &quot;)){\n                hashtable.put(i+&quot; &quot;,i);\n            }\n        }\n    }\n</code></pre>\n<p>如果一个containKey和一个contains，判断没有的话，就put值进去，但是会有一个问题，hashtable判断完后就释放锁了，因此两个方法都可能会导致hashtable和hashMap存在两个一模一样的key。因此hahmap足够用了。</p>\n<ul>\n<li>synchronizedMap  </li>\n</ul>\n<p>Collections.synchronized*(m)将线程不安全额集合变为线程安全集合</p>\n<ul>\n<li>synchronizedMap  </li>\n</ul>\n<p>ConcurrentHashMap<br>ConcurrentMap接口下有俩个重要的实现:<br>ConcurrentHashMap<br>ConcurrentskipListMap(支持并发排序功能。弥补ConcurrentHashMap)ConcurrentHashMap内部使用段(Segment)来表示这些不同的部分，每个段其实就是一个小的HashTable,它们有自己的锁。只要多个修改操作发生在不同的段上，它们就可以并发进行。把一个整体分成了16个段(Segment.也就是最高支持16个线程的并发修改操作。<br>这也是在重线程场景时减小锁的粒度从而降低锁竞争的一种方案。并且代码中大多共享变<br>量使用volatile关键字声明，目的是第一时间获取修改的内容，性能非常好。</p>\n<ul>\n<li>CountDownLatch<br>CountDownLatch类位于java.util.concurrent包下，利用它可以实现类似计数器的功能。比如有一个任务A，它要等待其他4个任务执行完毕之后才能执行，此时就可以利用CountDownLatch来实现这种功能了。<br>```java<br>package com.xuyiwei.my_03;</li>\n</ul>\n<p>import java.util.concurrent.CountDownLatch;</p>\n<p>/**</p>\n<ul>\n<li>Created by xuyiwei on 2019/2/13.<br>*/<br>public class CountDownLatchDemo {<br> public static void main(String[] args) throws InterruptedException {<pre><code> //这个计数器可以统计子线程的任务结束\n CountDownLatch latch = new CountDownLatch(2);\n new Thread(new Runnable() {\n     @Override\n     public void run() {\n         System.out.println(Thread.currentThread().getName()+&quot;开始执行&quot;);\n         latch.countDown();\n         System.out.println(Thread.currentThread().getName()+&quot;结束执行&quot;);\n     }\n }).start();\n new Thread(new Runnable() {\n     @Override\n     public void run() {\n         System.out.println(Thread.currentThread().getName()+&quot;开始执行&quot;);\n         latch.countDown();\n         System.out.println(Thread.currentThread().getName()+&quot;结束执行&quot;);\n     }\n }).start();\n //只有latch为0的时候 阻塞会变成运行状态\n latch.await();\n System.out.println(&quot;线程执行完毕&quot;);\n  /**\n  Thread-0开始执行\n  Thread-0结束执行\n  Thread-1开始执行\n  Thread-1结束执行\n  线程执行完毕\n  */\n</code></pre> }<br>}</li>\n</ul>\n<pre><code>\n- CyclicBarrier  \n\nCyclicBarrier初始化时规定一个数目，然后计算调用了CyclicBarrier.await()进入等待的线程数。当线程数达到了这个数目时，所有进入等待状态的线程被唤醒并继续。  \nCyclicBarrier就象它名字的意思一样，可看成是个障碍，所有的线程必须到齐后才能一起通过这个障碍。  \nCyclicBarrier初始时还可带一个Runnable的参数，此Runnable任务在CyclicBarrier的数目达到后，所有其它线程被唤醒前被执行。\n```java\npackage com.xuyiwei.my_03;\n\nimport java.util.concurrent.BrokenBarrierException;\nimport java.util.concurrent.CyclicBarrier;\n\n/**\n * Created by xuyiwei on 2019/2/13.\n */\nclass ThreadDemo extends Thread{\n    private CyclicBarrier cyclicBarrier;\n    public ThreadDemo(CyclicBarrier cyclicBarrier) {\n        this.cyclicBarrier = cyclicBarrier;\n    }\n    @Override\n    public void run() {\n        System.out.println(Thread.currentThread().getName()+&quot;开始写入&quot;);\n        try {\n            //CyclicBarrier初始化时规定一个数目，然后计算调用了CyclicBarrier.await()进入等待的线程数。\n            //当线程数达到了这个数目时，所有进入等待状态的线程被唤醒并继续\n            cyclicBarrier.await();\n        } catch (InterruptedException e) {\n            e.printStackTrace();\n        } catch (BrokenBarrierException e) {\n            e.printStackTrace();\n        }\n        System.out.println(Thread.currentThread().getName()+&quot;写入完毕&quot;);\n    }\n}\npublic class CyclicBarrierDemo {\n    public static void main(String[] args) {\n        CyclicBarrier cyclicBarrier = new CyclicBarrier(5);\n        ThreadDemo thread1 = new ThreadDemo(cyclicBarrier);\n        ThreadDemo thread2 = new ThreadDemo(cyclicBarrier);\n        ThreadDemo thread3 = new ThreadDemo(cyclicBarrier);\n        ThreadDemo thread4 = new ThreadDemo(cyclicBarrier);\n        ThreadDemo thread5 = new ThreadDemo(cyclicBarrier);\n        thread1.start();thread2.start();thread3.start();\n        thread4.start();thread5.start();\n        /**\n         Thread-0开始写入\n         Thread-1开始写入\n         Thread-2开始写入\n         Thread-3开始写入\n         Thread-4开始写入\n         Thread-4写入完毕\n         Thread-0写入完毕\n         Thread-1写入完毕\n         Thread-2写入完毕\n         Thread-3写入完毕\n         */\n    }\n}\n</code></pre><ul>\n<li>Semaphore</li>\n</ul>\n<p>Semaphore是一种基于计数的信号量。它可以设定一个阈值，基于此，多个线程竞争获取许可信号，做自己的申请后归还，超过阈值后，线程申请许可信号将会被阻塞。Semaphore可以用来构建一些对象池，资源池之类的，比如数据库连接池，我们也可以创建计数为1的Semaphore，将其作为一种类似互斥锁的机制，这也叫二元信号量，表示两种互斥状态。<br>它的用法如下：<br>availablePermits函数用来获取当前可用的资源数量<br>wc.acquire(); //申请资源<br>wc.release();// 释放资源  </p>\n<pre><code class=\"lang-java\">package com.xuyiwei.my_03;\n\nimport java.util.concurrent.Semaphore;\n\n/**\n * Created by xuyiwei on 2019/2/13.\n */\nclass Person implements Runnable{\n    private String name;\n    private Semaphore wc;\n    public Person(String name, Semaphore wc) {\n        this.name = name;\n        this.wc = wc;\n    }\n    @Override\n    public void run() {\n        System.out.println(name+&quot;想上厕所&quot;);\n        int i =  wc.availablePermits();\n        if(i&gt;0){\n            System.out.println(&quot;还有多的厕所&quot;);\n        }else{\n            System.out.println(&quot;没有多的厕所了&quot;);\n        }\n        try {\n            //// 一个厕所只有3个坑位，但是有10个人来上厕所，那怎么办？假设10的人的编号分别为1-10，并且1号先到厕所，10号最后到厕所。那么1-3号来的时候必然有可用坑位，顺利如厕，4号来的时候需要看看前面3人是否有人出来了，如果有人出来，进去，否则等待。同样的道理，4-10号也需要等待正在上厕所的人出来后才能进去，并且谁先进去这得看等待的人是否有素质，是否能遵守先来先上的规则。\n            wc.acquire();\n        } catch (InterruptedException e) {\n            e.printStackTrace();\n        }\n        try {\n            Thread.sleep(1000);\n        } catch (InterruptedException e) {\n            e.printStackTrace();\n        }\n        System.out.println(name+&quot;上完厕所了&quot;);\n        wc.release();\n\n    }\n}\npublic class SemaphoreDemo {\n    public static void main(String[] args) {\n        Semaphore semaphore = new Semaphore(3);\n        for(int i=0;i&lt;10;i++){\n            Person p1 = new Person(i+&quot;&quot;,semaphore);\n            Thread thread = new Thread(p1);\n            thread.start();\n        }\n    }\n}\n</code></pre>\n<ul>\n<li><h2 id=\"-\">并发队列</h2>\n在并发队列上JDK提供了两套实现，一个是以ConcurrentLinkedQueue为代表的高性能队列，一个是以BlockingQueue接口为代表的阻塞队列，无论哪种都继承自Queue。</li>\n</ul>\n<p><img src=\"http://xyw-blog.oss-cn-qingdao.aliyuncs.com/xblog/article/20190214103612869_image-1550111760658.png?Expires=1865471775&amp;OSSAccessKeyId=LTAIgFRAJMcFAUPX&amp;Signature=MHHbg5O%2Fz4k6g%2FCXf4kG15Fi6DU%3D\" alt=\"file\"></p>\n<ul>\n<li>ConcurrentLinkedDeque</li>\n</ul>\n<p>ConcurrentLinkedQueue:是一个适用于高并发场景下的队列，通过无锁的方式，实现了高并发状态下的高性能，通常ConcurrentLinkedQueue性能好于BlockingQueue.它是一个基于链接节点的无界线程安全队列。该队列的元素遵循先进先出的原则。头是最先加入的，尾是最近加入的，该队列不允许null元素。<br>ConcurrentLinkedQueue重要方法:add和offer()都是加入元素的方法(在ConcurrentLinkedQueue中这俩个方法没有任何区别)poll()和peek()都是取头元素节点，区别在于前者会删除元素，后者不会。</p>\n<pre><code class=\"lang-java\">package com.xuyiwei.my_03;\n\nimport java.util.concurrent.ConcurrentLinkedDeque;\n\n/**\n * Created by xuyiwei on 2019/2/13.\n */\npublic class ConcurrentLinkedDequeDemo {\n    public static void main(String[] args) {\n        ConcurrentLinkedDeque q = new ConcurrentLinkedDeque();\n        //add和offer没任何区别都是新增到队列 然后先进先出\n        q.add(&quot;1&quot;);\n        q.offer(&quot;2&quot;);\n        //push是直接把元素压到队列最前面相当于addFirst\n        q.push(&quot;3&quot;);\n        q.addLast(&quot;4&quot;);\n        System.out.println(q.poll());\n        System.out.println(q.poll());\n        System.out.println(q.poll());\n        System.out.println(q.poll());\n\n        q.add(&quot;1&quot;);\n        q.offer(&quot;2&quot;);\n        //从头获取元素,删除该元素\n        System.out.println(q.poll());\n        //从头获取元素,不刪除该元素\n        System.out.println(q.peek());\n        //获取总长度\n        System.out.println(&quot;size&quot;+q.size());\n        /**\n         3\n         1\n         2\n         4\n         1\n         2\n         size1\n         */\n    }\n}\n</code></pre>\n<ul>\n<li>BlockingQueue<br>ConcurrentskipListMap(支持并发排序功能。弥补ConcurrentHashMap)ConcurrentHashMap内部使用段(Segment)来表示这些不同的部分，每个段其实就是一个小的HashTable,它们有自己的锁。只要多个修改操作发生在不同的段上，它们就可以并发进行。把一个整体分成了16个段(Segment.也就是最高支持16个线程的并发修改操作。  </li>\n</ul>\n<p>阻塞队列（BlockingQueue）是一个支持两个附加操作的队列。这两个附加的操作是：<br>在队列为空时，获取元素的线程会等待队列变为非空。<br>当队列满时，存储元素的线程会等待队列可用。<br>阻塞队列常用于生产者和消费者的场景，生产者是往队列里添加元素的线程，消费者是从队列里拿元素的线程。阻塞队列就是生产者存放元素的容器，而消费者也只从容器里拿元素。</p>\n<p>BlockingQueue即阻塞队列，从阻塞这个词可以看出，在某些情况下对阻塞队列的访问可能会造成阻塞。被阻塞的情况主要有如下两种：</p>\n<ol>\n<li>当队列满了的时候进行入队列操作</li>\n<li><p>当队列空了的时候进行出队列操作<br>因此，当一个线程试图对一个已经满了的队列进行入队列操作时，它将会被阻塞，除非有另一个线程做了出队列操作；同样，当一个线程试图对一个空队列进行出队列操作时，它将会被阻塞，除非有另一个线程进行了入队列操作。<br>在Java中，BlockingQueue的接口位于java.util.concurrent 包中(在Java5版本开始提供)，由上面介绍的阻塞队列的特性可知，阻塞队列是线程安全的。<br>在新增的Concurrent包中，BlockingQueue很好的解决了多线程中，如何高效安全“传输”数据的问题。通过这些高效并且线程安全的队列类，为我们快速搭建高质量的多线程程序带来极大的便利。本文详细介绍了BlockingQueue家庭中的所有成员，包括他们各自的功能以及常见使用场景。<br>认识BlockingQueue<br>阻塞队列，顾名思义，首先它是一个队列，而一个队列在数据结构中所起的作用大致如下图所示：<br>从上图我们可以很清楚看到，通过一个共享的队列，可以使得数据由队列的一端输入，从另外一端输出；<br>常用的队列主要有以下两种：（当然通过不同的实现方式，还可以延伸出很多不同类型的队列，DelayQueue就是其中的一种）<br>先进先出（FIFO）：先插入的队列的元素也最先出队列，类似于排队的功能。从某种程度上来说这种队列也体现了一种公平性。<br>后进先出（LIFO）：后插入队列的元素最先出队列，这种队列优先处理最近发生的事件。<br>多线程环境中，通过队列可以很容易实现数据共享，比如经典的“生产者”和“消费者”模型中，通过队列可以很便利地实现两者之间的数据共享。假设我们有若干生产者线程，另外又有若干个消费者线程。如果生产者线程需要把准备好的数据共享给消费者线程，利用队列的方式来传递数据，就可以很方便地解决他们之间的数据共享问题。但如果生产者和消费者在某个时间段内，万一发生数据处理速度不匹配的情况呢？理想情况下，如果生产者产出数据的速度大于消费者消费的速度，并且当生产出来的数据累积到一定程度的时候，那么生产者必须暂停等待一下（阻塞生产者线程），以便等待消费者线程把累积的数据处理完毕，反之亦然。然而，在concurrent包发布以前，在多线程环境下，我们每个程序员都必须去自己控制这些细节，尤其还要兼顾效率和线程安全，而这会给我们的程序带来不小的复杂度。好在此时，强大的concurrent包横空出世了，而他也给我们带来了强大的BlockingQueue。（在多线程领域：所谓阻塞，在某些情况下会挂起线程（即阻塞），一旦条件满足，被挂起的线程又会自动被唤醒）<br>下面两幅图演示了BlockingQueue的两个常见阻塞场景：</p>\n</li>\n<li><p>ArrayBlockingQueue</p>\n</li>\n</ol>\n<p>ArrayBlockingQueue是一个有边界的阻塞队列，它的内部实现是一个数组。有边界的意思是它的容量是有限的，我们必须在其初始化的时候指定它的容量大小，容量大小一旦指定就不可改变。<br>ArrayBlockingQueue是以先进先出的方式存储数据，最新插入的对象是尾部，最新移出的对象是头部。下面</p>\n<pre><code class=\"lang-java\">package com.xuyiwei.my_03;\n\nimport java.util.concurrent.ArrayBlockingQueue;\nimport java.util.concurrent.BlockingQueue;\n\n/**\n * Created by xuyiwei on 2019/2/13.\n */\nclass ArrayBlockingThread extends Thread{\n    private BlockingQueue queue;\n\n    public ArrayBlockingThread(BlockingQueue queue) {\n        this.queue = queue;\n    }\n\n    @Override\n    public void run() {\n        //add的话 如果队列满了 就直接报错 Exception in thread &quot;Thread-0&quot; java.lang.IllegalStateException: Queue full\n        queue.add(11);\n        queue.add(11);\n//        queue.add(11);\n        //put的话 如果队列满了 会阻塞，直到队列有位置\n//        try {\n//            queue.put(11);\n//        } catch (InterruptedException e) {\n//            e.printStackTrace();\n//        }\n        //offer的话 如果队列满了 会返回false\n//        try {\n//            boolean a= queue.offer(11,2, TimeUnit.SECONDS);\n//            System.out.println(a);\n//        } catch (InterruptedException e) {\n//            e.printStackTrace();\n//        }\n\n//        poll: 若队列为空，返回null。\n//\n//        remove:若队列为空，抛出NoSuchElementException异常。\n//\n//        take:若队列为空，发生阻塞，等待有元素。\n\n    }\n}\npublic class ArrayBlockingQueueDemo {\n    public static void main(String[] args) {\n        ArrayBlockingQueue queue = new ArrayBlockingQueue(2);\n        ArrayBlockingThread thread = new ArrayBlockingThread(queue);\n        thread.start();\n    }\n}\n</code></pre>\n<ul>\n<li>LinkedBlockingQueue</li>\n</ul>\n<p>LinkedBlockingQueue阻塞队列大小的配置是可选的，如果我们初始化时指定一个大小，它就是有边界的，如果不指定，它就是无边界的。说是无边界，其实是采用了默认大小为Integer.MAX_VALUE的容量 。它的内部实现是一个链表。<br>和ArrayBlockingQueue一样，LinkedBlockingQueue 也是以先进先出的方式存储数据，最新插入的对象是尾部，最新移出的对象是头部。下面是一个初始化和使LinkedBlockingQueue的例子：</p>\n<pre><code class=\"lang-java\">package com.xuyiwei.my_03;\n\nimport java.util.concurrent.LinkedBlockingQueue;\n\n/**\n * Created by xuyiwei on 2019/2/14.\n */\npublic class LinkedBlockingQueueDemo {\n    public static void main(String[] args) {\n        LinkedBlockingQueue linkedBlockingQueue = new LinkedBlockingQueue(3);\n        linkedBlockingQueue.add(&quot;张三&quot;);\n        linkedBlockingQueue.add(&quot;李四&quot;);\n        linkedBlockingQueue.add(&quot;李四&quot;);\n        System.out.println(linkedBlockingQueue.size());\n    }\n}\n</code></pre>\n<ul>\n<li>PriorityBlockingQueue</li>\n</ul>\n<p>PriorityBlockingQueue是一个没有边界的队列，它的排序规则和java.util.PriorityQueue一样。需要注意，PriorityBlockingQueue中允许插入null对象。<br>所有插入PriorityBlockingQueue的对象必须实现 java.lang.Comparable接口，队列优先级的排序规则就是按照我们对这个接口的实现来定义的。<br>另外，我们可以从PriorityBlockingQueue获得一个迭代器Iterator，但这个迭代器并不保证按照优先级顺序进行迭代。   下面我们举个例子来说明一下，首先我们定义一个对象类型，这个对象需要实现Comparable接口</p>\n<ul>\n<li>SynchronousQueue     </li>\n</ul>\n<p>SynchronousQueue队列内部仅允许容纳一个元素。当一个线程插入一个元素后会被阻塞，除非这个元素被另一个线程消费</p>\n<ul>\n<li>使用BlockingQueue模拟生产者与消费者</li>\n</ul>\n<pre><code class=\"lang-java\">package com.xuyiwei.my_03;\n\nimport java.util.concurrent.BlockingQueue;\nimport java.util.concurrent.LinkedBlockingQueue;\nimport java.util.concurrent.TimeUnit;\n\n/**\n * Created by xuyiwei on 2019/2/13.\n * 生产者消费者例子\n */\nclass Customer extends Thread{\n    private BlockingQueue&lt;String&gt; queue;\n\n    public Customer(BlockingQueue&lt;String&gt; queue) {\n        this.queue = queue;\n    }\n\n    @Override\n    public void run() {\n        System.out.println(&quot;正准备吃东西&quot;);\n        while (true){\n            try {\n                String food = queue.poll(1, TimeUnit.SECONDS);\n                if(food==null){\n                    System.out.println(&quot;等了一分钟没上菜,继续等&quot;);\n\n                }else{\n                    System.out.println(&quot;开始吃&quot;+food);\n                    Thread.sleep(1000);\n                }\n             } catch (InterruptedException e) {\n                e.printStackTrace();\n                System.out.println(&quot;吃菜报异常&quot;);\n            }\n        }\n    }\n}\nclass Pruducer extends Thread{\n    private BlockingQueue&lt;String&gt; queue;\n\n    private String[] foods = {&quot;泡椒牛蛙&quot;,&quot;烤牛蛙&quot;,&quot;爆炒牛蛙&quot;};\n    public Pruducer(BlockingQueue&lt;String&gt; queue) {\n        this.queue = queue;\n    }\n\n    @Override\n    public void run() {\n        System.out.println(&quot;正准备做东西&quot;);\n\n        while (true){\n            try {\n                String f =foods[1+(int)(Math.random()*2)];\n                boolean food = queue.offer(f,1, TimeUnit.SECONDS);\n                if(food){\n                    System.out.println(&quot;上菜&quot;+f);\n                    Thread.sleep(1000);\n\n                }else{\n                    System.out.println(&quot;等了一分钟都没吃完,继续等&quot;);\n\n                }\n            } catch (InterruptedException e) {\n                e.printStackTrace();\n                System.out.println(&quot;上菜报异常&quot;);\n\n            }\n\n        }\n    }\n}\npublic class SendUseDemo {\n    public static void main(String[] args) {\n        LinkedBlockingQueue queue = new LinkedBlockingQueue(3);\n        Pruducer p = new Pruducer(queue);\n        Pruducer p1 = new Pruducer(queue);\n\n        Customer c = new Customer(queue);\n        p.start();\n        p1.start();\n        c.start();\n    }\n}\n</code></pre>\n<ul>\n<li><h2 id=\"-\">线程池</h2>\n</li>\n</ul>\n<p>Java中的线程池是运用场景最多的并发框架，几乎所有需要异步或并发执行任务的程序都可以使用线程池。在开发过程中，合理地使用线程池能够带来3个好处。<br>第一：降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。<br>第二：提高响应速度。当任务到达时，任务可以不需要等到线程创建就能立即执行。<br>第三：提高线程的可管理性。线程是稀缺资源，如果无限制地创建，不仅会消耗系统资源，<br>还会降低系统的稳定性，使用线程池可以进行统一分配、调优和监控。但是，要做到合理利用<br>线程池，必须对其实现原理了如指掌。</p>\n<ul>\n<li>线程池作用</li>\n</ul>\n<p>线程池是为突然大量爆发的线程设计的，通过有限的几个固定线程为大量的操作服务，减少了创建和销毁线程所需的时间，从而提高效率。<br>如果一个线程的时间非常长，就没必要用线程池了(不是不能作长时间操作，而是不宜。)，况且我们还不能控制线程池中线程的开始、挂起、和中止</p>\n<ul>\n<li><p>线程池的分类</p>\n</li>\n<li><p>ThreadPoolExecutor</p>\n</li>\n</ul>\n<p>Java是天生就支持并发的语言，支持并发意味着多线程，线程的频繁创建在高并发及大数据量是非常消耗资源的，因为java提供了线程池。在jdk1.5以前的版本中，线程池的使用是及其简陋的，但是在JDK1.5后，有了很大的改善。JDK1.5之后加入了java.util.concurrent包，java.util.concurrent包的加入给予开发人员开发并发程序以及解决并发问题很大的帮助。这篇文章主要介绍下并发包下的Executor接口，Executor接口虽然作为一个非常旧的接口（JDK1.52004年发布），但是很多程序员对于其中的一些原理还是不熟悉，因此写这篇文章来介绍下Executor接口，同时巩固下自己的知识。如果文章中有出现错误，欢迎大家指出。<br>Executor框架的最顶层实现是ThreadPoolExecutor类，Executors工厂类中提供的newScheduledThreadPool、newFixedThreadPool、newCachedThreadPool方法其实也只是ThreadPoolExecutor的构造函数参数不同而已。通过传入不同的参数，就可以构造出适用于不同应用场景下的线程池，那么它的底层原理是怎样实现的呢，这篇就来介绍下ThreadPoolExecutor线程池的运行过程。  </p>\n<font color=\'red\'><br>corePoolSize： 核心池的大小。     当有任务来之后，就会创建一个线程去执行任务，当线程池中的线程数目达到corePoolSize后，就会把到达的任务放到缓存队列当中<br>maximumPoolSize：线程池最大线程数，它表示在线程池中最多能创建多少个线程；<br>keepAliveTime：表示线程没有任务执行时最多保持多久时间会终止。<br>unit： 参数keepAliveTime的时间单位，有7种取值，在TimeUnit类中有7种静态属性：</font>\n\n\n<ul>\n<li>线程池四种创建方式</li>\n</ul>\n<p>Java通过Executors（jdk1.5并发包）提供四种线程池，分别为：<br>newCachedThreadPool<br>创建一个可缓存线程池，如果线程池长度超过处理需要，可灵活回收空闲线程，若无可回收，则新建线程。<br>案例演示:</p>\n<p>newFixedThreadPool<br>创建一个定长线程池，可控制线程最大并发数，超出的线程会在队列中等待。  </p>\n<p>newScheduledThreadPool<br>创建一个定长线程池，支持定时及周期性任务执行。  </p>\n<p>newSingleThreadExecutor<br>创建一个单线程化的线程池，它只会用唯一的工作线程来执行任务，保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。</p>\n<ul>\n<li>newCachedThreadPool  </li>\n</ul>\n<p>创建一个可缓存线程池，如果线程池长度超过处理需要，可灵活回收空闲线程，若无可回收，则新建线程。示例代码如下：</p>\n<pre><code class=\"lang-java\">package com.xuyiwei.my_03;\n\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\n\n/**\n * Created by xuyiwei on 2019/2/13.\n */\npublic class CacheTheadToolDemo {\n    public static void main(String[] args) {\n        ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();\n        for(int i=0;i&lt;10;i++){\n            final int temp = i;\n            try {\n                Thread.sleep(1000);\n            } catch (InterruptedException e) {\n                e.printStackTrace();\n            }\n            //总结: 线程池为无限大，当执行第二个任务时第一个任务已经完成，会复用执行第一个任务的线程，而不用每次新建线程。\n            newCachedThreadPool.execute(new Runnable() {\n\n                @Override\n                public void run() {\n                    try {\n                        Thread.sleep(100);\n                    } catch (Exception e) {\n                        // TODO: handle exception\n                    }\n                    System.out.println(Thread.currentThread().getName() + &quot;,i:&quot; + temp);\n\n                }\n            });\n\n        }\n    }\n    /**\n     * pool-1-thread-1,i:0\n     pool-1-thread-1,i:1\n     pool-1-thread-1,i:2\n     pool-1-thread-1,i:3\n     pool-1-thread-1,i:4\n     pool-1-thread-1,i:5\n     pool-1-thread-1,i:6\n     pool-1-thread-1,i:7\n     pool-1-thread-1,i:8\n     pool-1-thread-1,i:9\n     */\n}\n</code></pre>\n<ul>\n<li>newFixedThreadPool  </li>\n</ul>\n<p>创建一个定长线程池，可控制线程最大并发数，超出的线程会在队列中等待。示例代码如下：</p>\n<pre><code class=\"lang-java\">package com.xuyiwei.my_03;\n\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\n\n/**\n * Created by xuyiwei on 2019/2/13.\n */\npublic class FixedThreadPoolDemo {\n    public static void main(String[] args) {\n        ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(5);\n        for (int i = 0; i &lt; 10; i++) {\n            final int temp = i;\n            newFixedThreadPool.execute(new Runnable() {\n\n                @Override\n                public void run() {\n                    System.out.println(Thread.currentThread().getId() + &quot;,i:&quot; + temp);\n\n                }\n            });\n        }\n\n    }\n    /**\n     * 10,i:0\n     11,i:1\n     12,i:2\n     13,i:3\n     14,i:4\n     10,i:5\n     14,i:6\n     11,i:7\n     13,i:8\n     12,i:9\n\n     */\n}\n</code></pre>\n<p>总结:因为一共线程池的长度是5，因此该线程池最多只有5个线程可以同时执行。<br>定长线程池的大小最好根据系统资源进行设置。如Runtime.getRuntime().availableProcessors()</p>\n<ul>\n<li>newScheduledThreadPool</li>\n</ul>\n<p>创建一个定长线程池，支持定时及周期性任务执行。延迟执行示例代码如下：</p>\n<pre><code class=\"lang-java\">package com.xuyiwei.my_03;\n\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.ScheduledExecutorService;\nimport java.util.concurrent.TimeUnit;\n\n/**\n * Created by xuyiwei on 2019/2/13.\n */\npublic class ScheduledThreadPoolDemo {\n    public static void main(String[] args) {\n        //表示延迟3秒执行。\n        ScheduledExecutorService newScheduledThreadPool = Executors.newScheduledThreadPool(5);\n        for (int i = 0; i &lt; 10; i++) {\n            final int temp = i;\n            newScheduledThreadPool.schedule(new Runnable() {\n                public void run() {\n                    System.out.println(&quot;i:&quot; + temp);\n                }\n            }, 3, TimeUnit.SECONDS);\n        }\n\n    }\n}\n</code></pre>\n<ul>\n<li>newSingleThreadExecutor</li>\n</ul>\n<p>创建一个单线程化的线程池，它只会用唯一的工作线程来执行任务，保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。示例代码如下：</p>\n<pre><code class=\"lang-java\">package com.xuyiwei.my_03;\n\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\n\n/**\n * Created by xuyiwei on 2019/2/13.\n */\npublic class SingleThreadExecutorDemo {\n    public static void main(String[] args) {\n        ExecutorService newSingleThreadExecutor = Executors.newSingleThreadExecutor();\n        for (int i = 0; i &lt; 10; i++) {\n            final int index = i;\n            newSingleThreadExecutor.execute(new Runnable() {\n                //注意: 结果依次输出，相当于顺序执行各个任务。\n                @Override\n                public void run() {\n                    System.out.println(&quot;index:&quot; + index);\n                    try {\n                        Thread.sleep(200);\n                    } catch (Exception e) {\n                        // TODO: handle exception\n                    }\n                }\n            });\n        }\n    }\n}\n</code></pre>\n<p>注意: 结果依次输出，相当于顺序执行各个任务。</p>\n<ul>\n<li>线程池原理剖析  </li>\n</ul>\n<p>提交一个任务到线程池中，线程池的处理流程如下：<br>1、判断线程池里的核心线程是否都在执行任务，如果不是（核心线程空闲或者还有核心线程没有被创建）则创建一个新的工作线程来执行任务。如果核心线程都在执行任务，则进入下个流程。<br>2、线程池判断工作队列是否已满，如果工作队列没有满，则将新提交的任务存储在这个工作队列里。如果工作队列满了，则进入下个流程。<br>3、判断线程池里的线程是否都处于工作状态，如果没有，则创建一个新的工作线程来执行任务。如果已经满了，则交给饱和策略来处理这个任务。</p>\n<ul>\n<li>合理配置线程池</li>\n</ul>\n<p>要想合理的配置线程池，就必须首先分析任务特性，可以从以下几个角度来进行分析：<br>任务的性质：CPU密集型任务，IO密集型任务和混合型任务。<br>任务的优先级：高，中和低。<br>任务的执行时间：长，中和短。<br>任务的依赖性：是否依赖其他系统资源，如数据库连接。<br>任务性质不同的任务可以用不同规模的线程池分开处理。CPU密集型任务配置尽可能少的线程数量，如配置Ncpu+1个线程的线程池。IO密集型任务则由于需要等待IO操作，线程并不是一直在执行任务，则配置尽可能多的线程，如2*Ncpu。混合型的任务，如果可以拆分，则将其拆分成一个CPU密集型任务和一个IO密集型任务，只要这两个任务执行的时间相差不是太大，那么分解后执行的吞吐率要高于串行执行的吞吐率，如果这两个任务执行时间相差太大，则没必要进行分解。我们可以通过Runtime.getRuntime().availableProcessors()方法获得当前设备的CPU个数。<br>优先级不同的任务可以使用优先级队列PriorityBlockingQueue来处理。它可以让优先级高的任务先得到执行，需要注意的是如果一直有优先级高的任务提交到队列里，那么优先级低的任务可能永远不能执行。<br>执行时间不同的任务可以交给不同规模的线程池来处理，或者也可以使用优先级队列，让执行时间短的任务先执行。<br>依赖数据库连接池的任务，因为线程提交SQL后需要等待数据库返回结果，如果等待的时间越长CPU空闲时间就越长，那么线程数应该设置越大，这样才能更好的利用CPU。  </p>\n<p>CPU密集型时，任务可以少配置线程数，大概和机器的cpu核数相当，这样可以使得每个线程都在执行任务<br>IO密集型时，大部分线程都阻塞，故需要多配置线程数，2*cpu核数<br>操作系统之名称解释：<br>某些进程花费了绝大多数时间在计算上，而其他则在等待I/O上花费了大多是时间，前者称为计算密集型（CPU密集型）computer-bound，后者称为I/O密集型，I/O-bound。</p>\n', '- ## 同步容器类\n- Vector与ArrayList区别 \n\n1.ArrayList是最常用的List实现类，内部是通过数组实现的，它允许对元素进行快速随机访问。数组的缺点是每个元素之间不能有间隔，当数组大小不满足时需要增加存储能力，就要讲已经有数组的数据复制到新的存储空间中。当从ArrayList的中间位置插入或者删除元素时，需要对数组进行复制、移动、代价比较高。因此，它适合随机查找和遍历，不适合插入和删除。  \n2.Vector与ArrayList一样，也是通过数组实现的，不同的是它支持线程的同步，即某一时刻只有一个线程能够写Vector，避免多线程同时写而引起的不一致性，但实现同步需要很高的花费，因此，访问它比访问ArrayList慢  \n注意: Vector线程安全、ArrayList  \n\n```java\npackage com.xuyiwei.my_03;\n\nimport java.util.ArrayList;\nimport java.util.Vector;\n\n/**\n * Created by xuyiwei on 2019/2/3.\n */\npublic class ListDemo extends Thread{\n    ArrayList<Integer> arrayList;\n    Vector<Integer> vector;\n    public ListDemo(ArrayList<Integer> arrayList, Vector<Integer> vector) {\n        this.arrayList = arrayList;\n        this.vector = vector;\n    }\n    @Override\n    public void run() {\n        for(int i=0;i<20;i++){\n            arrayList.add(i );\n            vector.add(i );\n        }\n    }\n\n    public static void main(String[] args) {\n        ArrayList<Integer> list = new ArrayList<>();\n        Vector<Integer> vector = new Vector<>();\n        ListDemo demo = new ListDemo(list,vector);\n        ListDemo demo1 = new ListDemo(list,vector);\n        demo.start();\n        demo1.start();\n        try {\n            Thread.sleep(3000);\n        } catch (InterruptedException e) {\n            e.printStackTrace();\n        }\n        //因为list线程不安全  因此会导致有些值add不进去 甚至报错现象 因为arrayList本质还是数组  数组下标多个线程同时操作会出问题\n        System.out.println(\"listSize:\"+list.size()+\" \");\n        for(Integer i :list){\n            System.out.print(\"list:\"+i+\" \");\n        }\n        System.out.println(\" \");\n        //因为vector线程安全 所以所有值都能add进去 就是20个\n        System.out.println(\"vectorSize:\"+vector.size()+\" \");\n\n        for(Integer i :vector){\n            System.out.print(\"vector:\"+i+\" \");\n        }\n\n        /**\n         listSize:39\n         list:0 list:0 list:1 list:2 list:1 list:3 list:2 list:4 list:3 list:5 list:4 list:5 list:7 list:6 list:8 list:null list:9 list:8 list:10 list:9 list:11 list:10 list:null list:11 list:13 list:12 list:14 list:13 list:15 list:14 list:16 list:15 list:17 list:null list:18 list:17 list:19 list:18 list:19\n         vectorSize:40\n         vector:0 vector:1 vector:0 vector:2 vector:1 vector:3 vector:2 vector:4 vector:5 vector:3 vector:4 vector:6 vector:5 vector:7 vector:6 vector:8 vector:7 vector:9 vector:8 vector:10 vector:9 vector:11 vector:10 vector:12 vector:11 vector:13 vector:12 vector:14 vector:13 vector:15 vector:14 vector:16 vector:15 vector:17 vector:16 vector:18 vector:17 vector:19 vector:18 vector:19\n         */\n    }\n}\n\n\n```\n\n- Vector源码类 \n\nAdd方法源码类\n![file](http://xyw-blog.oss-cn-qingdao.aliyuncs.com/xblog/article/20190203155626103_image-1549180585637.png?Expires=1864540586&OSSAccessKeyId=LTAIgFRAJMcFAUPX&Signature=1hMjZlyLNgDkpe8vVuBE7VzcpGc%3D)\n- Arraylist源码  \n\nAdd方法源码类\n![file](http://xyw-blog.oss-cn-qingdao.aliyuncs.com/xblog/article/20190203155713080_image-1549180632866.png?Expires=1864540633&OSSAccessKeyId=LTAIgFRAJMcFAUPX&Signature=PeajLdiVbwSeVHtmvPLd2zvbAA8%3D)\n\n- HasTable与HasMap  \n\n1.HashMap不是线程安全的   \nHastMap是一个接口是map接口的子接口，是将键映射到值的对象，其中键和值都是对象，并且不能包含重复键，但可以包含重复值。HashMap允许null key和null value，而hashtable不允许。  \n2.HashTable是线程安全的一个Collection。  \n3.HashMap是Hashtable的轻量级实现（非线程安全的实现），他们都完成了Map接口，主要区别在于HashMap允许空（null）键值（key）,由于非线程安全，效率上可能高于Hashtable。HashMap允许将null作为一个entry的key或者value，而Hashtable不允许。HashMap把Hashtable的contains方法去掉了，改成containsvalue和containsKey。    \n注意: HashTable线程安全，HashMap线程不安全。\n源码分析\n其实仔细一想虽然HashTable线程安全，但是并没有多大用处，他只能保证HashTable执行单个方法的时候线程安全，但是没法确保一个方法里面都安全。\n```java\n  public void run() {\n        for(int i=0;i<1000;i++){\n            if(!hashMap.containsKey(i+\" \")){\n                hashMap.put(i+\" \",i);\n            }\n            if(!hashtable.contains(i+\" \")){\n                hashtable.put(i+\" \",i);\n            }\n        }\n    }\n```\n如果一个containKey和一个contains，判断没有的话，就put值进去，但是会有一个问题，hashtable判断完后就释放锁了，因此两个方法都可能会导致hashtable和hashMap存在两个一模一样的key。因此hahmap足够用了。\n\n- synchronizedMap  \n\nCollections.synchronized*(m)将线程不安全额集合变为线程安全集合\n\n- synchronizedMap  \n\nConcurrentHashMap  \nConcurrentMap接口下有俩个重要的实现:  \nConcurrentHashMap  \nConcurrentskipListMap(支持并发排序功能。弥补ConcurrentHashMap)ConcurrentHashMap内部使用段(Segment)来表示这些不同的部分，每个段其实就是一个小的HashTable,它们有自己的锁。只要多个修改操作发生在不同的段上，它们就可以并发进行。把一个整体分成了16个段(Segment.也就是最高支持16个线程的并发修改操作。  \n这也是在重线程场景时减小锁的粒度从而降低锁竞争的一种方案。并且代码中大多共享变\n量使用volatile关键字声明，目的是第一时间获取修改的内容，性能非常好。\n\n- CountDownLatch  \nCountDownLatch类位于java.util.concurrent包下，利用它可以实现类似计数器的功能。比如有一个任务A，它要等待其他4个任务执行完毕之后才能执行，此时就可以利用CountDownLatch来实现这种功能了。\n```java \npackage com.xuyiwei.my_03;\n\nimport java.util.concurrent.CountDownLatch;\n\n/**\n * Created by xuyiwei on 2019/2/13.\n */\npublic class CountDownLatchDemo {\n    public static void main(String[] args) throws InterruptedException {\n        //这个计数器可以统计子线程的任务结束\n        CountDownLatch latch = new CountDownLatch(2);\n        new Thread(new Runnable() {\n            @Override\n            public void run() {\n                System.out.println(Thread.currentThread().getName()+\"开始执行\");\n                latch.countDown();\n                System.out.println(Thread.currentThread().getName()+\"结束执行\");\n            }\n        }).start();\n        new Thread(new Runnable() {\n            @Override\n            public void run() {\n                System.out.println(Thread.currentThread().getName()+\"开始执行\");\n                latch.countDown();\n                System.out.println(Thread.currentThread().getName()+\"结束执行\");\n            }\n        }).start();\n        //只有latch为0的时候 阻塞会变成运行状态\n        latch.await();\n        System.out.println(\"线程执行完毕\");\n         /**\n         Thread-0开始执行\n         Thread-0结束执行\n         Thread-1开始执行\n         Thread-1结束执行\n         线程执行完毕\n         */\n    }\n}\n\n```\n\n- CyclicBarrier  \n\nCyclicBarrier初始化时规定一个数目，然后计算调用了CyclicBarrier.await()进入等待的线程数。当线程数达到了这个数目时，所有进入等待状态的线程被唤醒并继续。  \nCyclicBarrier就象它名字的意思一样，可看成是个障碍，所有的线程必须到齐后才能一起通过这个障碍。  \nCyclicBarrier初始时还可带一个Runnable的参数，此Runnable任务在CyclicBarrier的数目达到后，所有其它线程被唤醒前被执行。\n```java\npackage com.xuyiwei.my_03;\n\nimport java.util.concurrent.BrokenBarrierException;\nimport java.util.concurrent.CyclicBarrier;\n\n/**\n * Created by xuyiwei on 2019/2/13.\n */\nclass ThreadDemo extends Thread{\n    private CyclicBarrier cyclicBarrier;\n    public ThreadDemo(CyclicBarrier cyclicBarrier) {\n        this.cyclicBarrier = cyclicBarrier;\n    }\n    @Override\n    public void run() {\n        System.out.println(Thread.currentThread().getName()+\"开始写入\");\n        try {\n            //CyclicBarrier初始化时规定一个数目，然后计算调用了CyclicBarrier.await()进入等待的线程数。\n            //当线程数达到了这个数目时，所有进入等待状态的线程被唤醒并继续\n            cyclicBarrier.await();\n        } catch (InterruptedException e) {\n            e.printStackTrace();\n        } catch (BrokenBarrierException e) {\n            e.printStackTrace();\n        }\n        System.out.println(Thread.currentThread().getName()+\"写入完毕\");\n    }\n}\npublic class CyclicBarrierDemo {\n    public static void main(String[] args) {\n        CyclicBarrier cyclicBarrier = new CyclicBarrier(5);\n        ThreadDemo thread1 = new ThreadDemo(cyclicBarrier);\n        ThreadDemo thread2 = new ThreadDemo(cyclicBarrier);\n        ThreadDemo thread3 = new ThreadDemo(cyclicBarrier);\n        ThreadDemo thread4 = new ThreadDemo(cyclicBarrier);\n        ThreadDemo thread5 = new ThreadDemo(cyclicBarrier);\n        thread1.start();thread2.start();thread3.start();\n        thread4.start();thread5.start();\n        /**\n         Thread-0开始写入\n         Thread-1开始写入\n         Thread-2开始写入\n         Thread-3开始写入\n         Thread-4开始写入\n         Thread-4写入完毕\n         Thread-0写入完毕\n         Thread-1写入完毕\n         Thread-2写入完毕\n         Thread-3写入完毕\n         */\n    }\n}\n```\n\n- Semaphore\n\nSemaphore是一种基于计数的信号量。它可以设定一个阈值，基于此，多个线程竞争获取许可信号，做自己的申请后归还，超过阈值后，线程申请许可信号将会被阻塞。Semaphore可以用来构建一些对象池，资源池之类的，比如数据库连接池，我们也可以创建计数为1的Semaphore，将其作为一种类似互斥锁的机制，这也叫二元信号量，表示两种互斥状态。  \n它的用法如下：  \navailablePermits函数用来获取当前可用的资源数量   \nwc.acquire(); //申请资源   \nwc.release();// 释放资源  \n\n```java\npackage com.xuyiwei.my_03;\n\nimport java.util.concurrent.Semaphore;\n\n/**\n * Created by xuyiwei on 2019/2/13.\n */\nclass Person implements Runnable{\n    private String name;\n    private Semaphore wc;\n    public Person(String name, Semaphore wc) {\n        this.name = name;\n        this.wc = wc;\n    }\n    @Override\n    public void run() {\n        System.out.println(name+\"想上厕所\");\n        int i =  wc.availablePermits();\n        if(i>0){\n            System.out.println(\"还有多的厕所\");\n        }else{\n            System.out.println(\"没有多的厕所了\");\n        }\n        try {\n            //// 一个厕所只有3个坑位，但是有10个人来上厕所，那怎么办？假设10的人的编号分别为1-10，并且1号先到厕所，10号最后到厕所。那么1-3号来的时候必然有可用坑位，顺利如厕，4号来的时候需要看看前面3人是否有人出来了，如果有人出来，进去，否则等待。同样的道理，4-10号也需要等待正在上厕所的人出来后才能进去，并且谁先进去这得看等待的人是否有素质，是否能遵守先来先上的规则。\n            wc.acquire();\n        } catch (InterruptedException e) {\n            e.printStackTrace();\n        }\n        try {\n            Thread.sleep(1000);\n        } catch (InterruptedException e) {\n            e.printStackTrace();\n        }\n        System.out.println(name+\"上完厕所了\");\n        wc.release();\n\n    }\n}\npublic class SemaphoreDemo {\n    public static void main(String[] args) {\n        Semaphore semaphore = new Semaphore(3);\n        for(int i=0;i<10;i++){\n            Person p1 = new Person(i+\"\",semaphore);\n            Thread thread = new Thread(p1);\n            thread.start();\n        }\n    }\n}\n\n```\n\n- ## 并发队列\n在并发队列上JDK提供了两套实现，一个是以ConcurrentLinkedQueue为代表的高性能队列，一个是以BlockingQueue接口为代表的阻塞队列，无论哪种都继承自Queue。\n\n![file](http://xyw-blog.oss-cn-qingdao.aliyuncs.com/xblog/article/20190214103612869_image-1550111760658.png?Expires=1865471775&OSSAccessKeyId=LTAIgFRAJMcFAUPX&Signature=MHHbg5O%2Fz4k6g%2FCXf4kG15Fi6DU%3D)\n\n- ConcurrentLinkedDeque\n\nConcurrentLinkedQueue:是一个适用于高并发场景下的队列，通过无锁的方式，实现了高并发状态下的高性能，通常ConcurrentLinkedQueue性能好于BlockingQueue.它是一个基于链接节点的无界线程安全队列。该队列的元素遵循先进先出的原则。头是最先加入的，尾是最近加入的，该队列不允许null元素。  \nConcurrentLinkedQueue重要方法:add和offer()都是加入元素的方法(在ConcurrentLinkedQueue中这俩个方法没有任何区别)poll()和peek()都是取头元素节点，区别在于前者会删除元素，后者不会。\n\n```java\npackage com.xuyiwei.my_03;\n\nimport java.util.concurrent.ConcurrentLinkedDeque;\n\n/**\n * Created by xuyiwei on 2019/2/13.\n */\npublic class ConcurrentLinkedDequeDemo {\n    public static void main(String[] args) {\n        ConcurrentLinkedDeque q = new ConcurrentLinkedDeque();\n        //add和offer没任何区别都是新增到队列 然后先进先出\n        q.add(\"1\");\n        q.offer(\"2\");\n        //push是直接把元素压到队列最前面相当于addFirst\n        q.push(\"3\");\n        q.addLast(\"4\");\n        System.out.println(q.poll());\n        System.out.println(q.poll());\n        System.out.println(q.poll());\n        System.out.println(q.poll());\n\n        q.add(\"1\");\n        q.offer(\"2\");\n        //从头获取元素,删除该元素\n        System.out.println(q.poll());\n        //从头获取元素,不刪除该元素\n        System.out.println(q.peek());\n        //获取总长度\n        System.out.println(\"size\"+q.size());\n        /**\n         3\n         1\n         2\n         4\n         1\n         2\n         size1\n         */\n    }\n}\n\n```\n\n- BlockingQueue\nConcurrentskipListMap(支持并发排序功能。弥补ConcurrentHashMap)ConcurrentHashMap内部使用段(Segment)来表示这些不同的部分，每个段其实就是一个小的HashTable,它们有自己的锁。只要多个修改操作发生在不同的段上，它们就可以并发进行。把一个整体分成了16个段(Segment.也就是最高支持16个线程的并发修改操作。  \n\n\n阻塞队列（BlockingQueue）是一个支持两个附加操作的队列。这两个附加的操作是：  \n在队列为空时，获取元素的线程会等待队列变为非空。     \n当队列满时，存储元素的线程会等待队列可用。  \n阻塞队列常用于生产者和消费者的场景，生产者是往队列里添加元素的线程，消费者是从队列里拿元素的线程。阻塞队列就是生产者存放元素的容器，而消费者也只从容器里拿元素。\n\nBlockingQueue即阻塞队列，从阻塞这个词可以看出，在某些情况下对阻塞队列的访问可能会造成阻塞。被阻塞的情况主要有如下两种：\n1. 当队列满了的时候进行入队列操作\n2. 当队列空了的时候进行出队列操作\n因此，当一个线程试图对一个已经满了的队列进行入队列操作时，它将会被阻塞，除非有另一个线程做了出队列操作；同样，当一个线程试图对一个空队列进行出队列操作时，它将会被阻塞，除非有另一个线程进行了入队列操作。  \n在Java中，BlockingQueue的接口位于java.util.concurrent 包中(在Java5版本开始提供)，由上面介绍的阻塞队列的特性可知，阻塞队列是线程安全的。  \n在新增的Concurrent包中，BlockingQueue很好的解决了多线程中，如何高效安全“传输”数据的问题。通过这些高效并且线程安全的队列类，为我们快速搭建高质量的多线程程序带来极大的便利。本文详细介绍了BlockingQueue家庭中的所有成员，包括他们各自的功能以及常见使用场景。  \n认识BlockingQueue  \n阻塞队列，顾名思义，首先它是一个队列，而一个队列在数据结构中所起的作用大致如下图所示：\n从上图我们可以很清楚看到，通过一个共享的队列，可以使得数据由队列的一端输入，从另外一端输出；  \n常用的队列主要有以下两种：（当然通过不同的实现方式，还可以延伸出很多不同类型的队列，DelayQueue就是其中的一种）  \n先进先出（FIFO）：先插入的队列的元素也最先出队列，类似于排队的功能。从某种程度上来说这种队列也体现了一种公平性。  \n后进先出（LIFO）：后插入队列的元素最先出队列，这种队列优先处理最近发生的事件。  \n多线程环境中，通过队列可以很容易实现数据共享，比如经典的“生产者”和“消费者”模型中，通过队列可以很便利地实现两者之间的数据共享。假设我们有若干生产者线程，另外又有若干个消费者线程。如果生产者线程需要把准备好的数据共享给消费者线程，利用队列的方式来传递数据，就可以很方便地解决他们之间的数据共享问题。但如果生产者和消费者在某个时间段内，万一发生数据处理速度不匹配的情况呢？理想情况下，如果生产者产出数据的速度大于消费者消费的速度，并且当生产出来的数据累积到一定程度的时候，那么生产者必须暂停等待一下（阻塞生产者线程），以便等待消费者线程把累积的数据处理完毕，反之亦然。然而，在concurrent包发布以前，在多线程环境下，我们每个程序员都必须去自己控制这些细节，尤其还要兼顾效率和线程安全，而这会给我们的程序带来不小的复杂度。好在此时，强大的concurrent包横空出世了，而他也给我们带来了强大的BlockingQueue。（在多线程领域：所谓阻塞，在某些情况下会挂起线程（即阻塞），一旦条件满足，被挂起的线程又会自动被唤醒）  \n下面两幅图演示了BlockingQueue的两个常见阻塞场景：\n\n- ArrayBlockingQueue\n\nArrayBlockingQueue是一个有边界的阻塞队列，它的内部实现是一个数组。有边界的意思是它的容量是有限的，我们必须在其初始化的时候指定它的容量大小，容量大小一旦指定就不可改变。  \nArrayBlockingQueue是以先进先出的方式存储数据，最新插入的对象是尾部，最新移出的对象是头部。下面\n\n```java\npackage com.xuyiwei.my_03;\n\nimport java.util.concurrent.ArrayBlockingQueue;\nimport java.util.concurrent.BlockingQueue;\n\n/**\n * Created by xuyiwei on 2019/2/13.\n */\nclass ArrayBlockingThread extends Thread{\n    private BlockingQueue queue;\n\n    public ArrayBlockingThread(BlockingQueue queue) {\n        this.queue = queue;\n    }\n\n    @Override\n    public void run() {\n        //add的话 如果队列满了 就直接报错 Exception in thread \"Thread-0\" java.lang.IllegalStateException: Queue full\n        queue.add(11);\n        queue.add(11);\n//        queue.add(11);\n        //put的话 如果队列满了 会阻塞，直到队列有位置\n//        try {\n//            queue.put(11);\n//        } catch (InterruptedException e) {\n//            e.printStackTrace();\n//        }\n        //offer的话 如果队列满了 会返回false\n//        try {\n//            boolean a= queue.offer(11,2, TimeUnit.SECONDS);\n//            System.out.println(a);\n//        } catch (InterruptedException e) {\n//            e.printStackTrace();\n//        }\n\n//        poll: 若队列为空，返回null。\n//\n//        remove:若队列为空，抛出NoSuchElementException异常。\n//\n//        take:若队列为空，发生阻塞，等待有元素。\n\n    }\n}\npublic class ArrayBlockingQueueDemo {\n    public static void main(String[] args) {\n        ArrayBlockingQueue queue = new ArrayBlockingQueue(2);\n        ArrayBlockingThread thread = new ArrayBlockingThread(queue);\n        thread.start();\n    }\n}\n\n```\n\n- LinkedBlockingQueue\n\nLinkedBlockingQueue阻塞队列大小的配置是可选的，如果我们初始化时指定一个大小，它就是有边界的，如果不指定，它就是无边界的。说是无边界，其实是采用了默认大小为Integer.MAX_VALUE的容量 。它的内部实现是一个链表。  \n和ArrayBlockingQueue一样，LinkedBlockingQueue 也是以先进先出的方式存储数据，最新插入的对象是尾部，最新移出的对象是头部。下面是一个初始化和使LinkedBlockingQueue的例子：\n```java\npackage com.xuyiwei.my_03;\n\nimport java.util.concurrent.LinkedBlockingQueue;\n\n/**\n * Created by xuyiwei on 2019/2/14.\n */\npublic class LinkedBlockingQueueDemo {\n    public static void main(String[] args) {\n        LinkedBlockingQueue linkedBlockingQueue = new LinkedBlockingQueue(3);\n        linkedBlockingQueue.add(\"张三\");\n        linkedBlockingQueue.add(\"李四\");\n        linkedBlockingQueue.add(\"李四\");\n        System.out.println(linkedBlockingQueue.size());\n    }\n}\n\n```\n\n- PriorityBlockingQueue\n\nPriorityBlockingQueue是一个没有边界的队列，它的排序规则和java.util.PriorityQueue一样。需要注意，PriorityBlockingQueue中允许插入null对象。  \n所有插入PriorityBlockingQueue的对象必须实现 java.lang.Comparable接口，队列优先级的排序规则就是按照我们对这个接口的实现来定义的。  \n另外，我们可以从PriorityBlockingQueue获得一个迭代器Iterator，但这个迭代器并不保证按照优先级顺序进行迭代。   下面我们举个例子来说明一下，首先我们定义一个对象类型，这个对象需要实现Comparable接口\n\n- SynchronousQueue     \n\nSynchronousQueue队列内部仅允许容纳一个元素。当一个线程插入一个元素后会被阻塞，除非这个元素被另一个线程消费\n\n- 使用BlockingQueue模拟生产者与消费者\n\n```java\npackage com.xuyiwei.my_03;\n\nimport java.util.concurrent.BlockingQueue;\nimport java.util.concurrent.LinkedBlockingQueue;\nimport java.util.concurrent.TimeUnit;\n\n/**\n * Created by xuyiwei on 2019/2/13.\n * 生产者消费者例子\n */\nclass Customer extends Thread{\n    private BlockingQueue<String> queue;\n\n    public Customer(BlockingQueue<String> queue) {\n        this.queue = queue;\n    }\n\n    @Override\n    public void run() {\n        System.out.println(\"正准备吃东西\");\n        while (true){\n            try {\n                String food = queue.poll(1, TimeUnit.SECONDS);\n                if(food==null){\n                    System.out.println(\"等了一分钟没上菜,继续等\");\n\n                }else{\n                    System.out.println(\"开始吃\"+food);\n                    Thread.sleep(1000);\n                }\n             } catch (InterruptedException e) {\n                e.printStackTrace();\n                System.out.println(\"吃菜报异常\");\n            }\n        }\n    }\n}\nclass Pruducer extends Thread{\n    private BlockingQueue<String> queue;\n\n    private String[] foods = {\"泡椒牛蛙\",\"烤牛蛙\",\"爆炒牛蛙\"};\n    public Pruducer(BlockingQueue<String> queue) {\n        this.queue = queue;\n    }\n\n    @Override\n    public void run() {\n        System.out.println(\"正准备做东西\");\n\n        while (true){\n            try {\n                String f =foods[1+(int)(Math.random()*2)];\n                boolean food = queue.offer(f,1, TimeUnit.SECONDS);\n                if(food){\n                    System.out.println(\"上菜\"+f);\n                    Thread.sleep(1000);\n\n                }else{\n                    System.out.println(\"等了一分钟都没吃完,继续等\");\n\n                }\n            } catch (InterruptedException e) {\n                e.printStackTrace();\n                System.out.println(\"上菜报异常\");\n\n            }\n\n        }\n    }\n}\npublic class SendUseDemo {\n    public static void main(String[] args) {\n        LinkedBlockingQueue queue = new LinkedBlockingQueue(3);\n        Pruducer p = new Pruducer(queue);\n        Pruducer p1 = new Pruducer(queue);\n\n        Customer c = new Customer(queue);\n        p.start();\n        p1.start();\n        c.start();\n    }\n}\n\n```\n\n- ## 线程池\n\nJava中的线程池是运用场景最多的并发框架，几乎所有需要异步或并发执行任务的程序都可以使用线程池。在开发过程中，合理地使用线程池能够带来3个好处。  \n第一：降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。  \n第二：提高响应速度。当任务到达时，任务可以不需要等到线程创建就能立即执行。  \n第三：提高线程的可管理性。线程是稀缺资源，如果无限制地创建，不仅会消耗系统资源，\n还会降低系统的稳定性，使用线程池可以进行统一分配、调优和监控。但是，要做到合理利用\n线程池，必须对其实现原理了如指掌。\n\n- 线程池作用\n\n线程池是为突然大量爆发的线程设计的，通过有限的几个固定线程为大量的操作服务，减少了创建和销毁线程所需的时间，从而提高效率。  \n如果一个线程的时间非常长，就没必要用线程池了(不是不能作长时间操作，而是不宜。)，况且我们还不能控制线程池中线程的开始、挂起、和中止\n\n- 线程池的分类\n\n- ThreadPoolExecutor\n\nJava是天生就支持并发的语言，支持并发意味着多线程，线程的频繁创建在高并发及大数据量是非常消耗资源的，因为java提供了线程池。在jdk1.5以前的版本中，线程池的使用是及其简陋的，但是在JDK1.5后，有了很大的改善。JDK1.5之后加入了java.util.concurrent包，java.util.concurrent包的加入给予开发人员开发并发程序以及解决并发问题很大的帮助。这篇文章主要介绍下并发包下的Executor接口，Executor接口虽然作为一个非常旧的接口（JDK1.52004年发布），但是很多程序员对于其中的一些原理还是不熟悉，因此写这篇文章来介绍下Executor接口，同时巩固下自己的知识。如果文章中有出现错误，欢迎大家指出。  \nExecutor框架的最顶层实现是ThreadPoolExecutor类，Executors工厂类中提供的newScheduledThreadPool、newFixedThreadPool、newCachedThreadPool方法其实也只是ThreadPoolExecutor的构造函数参数不同而已。通过传入不同的参数，就可以构造出适用于不同应用场景下的线程池，那么它的底层原理是怎样实现的呢，这篇就来介绍下ThreadPoolExecutor线程池的运行过程。  \n\n<font color=\'red\'>\ncorePoolSize： 核心池的大小。     当有任务来之后，就会创建一个线程去执行任务，当线程池中的线程数目达到corePoolSize后，就会把到达的任务放到缓存队列当中  \nmaximumPoolSize：线程池最大线程数，它表示在线程池中最多能创建多少个线程；  \nkeepAliveTime：表示线程没有任务执行时最多保持多久时间会终止。  \nunit： 参数keepAliveTime的时间单位，有7种取值，在TimeUnit类中有7种静态属性：</font>\n\n\n- 线程池四种创建方式\n\nJava通过Executors（jdk1.5并发包）提供四种线程池，分别为：   \nnewCachedThreadPool  \n创建一个可缓存线程池，如果线程池长度超过处理需要，可灵活回收空闲线程，若无可回收，则新建线程。\n案例演示:\n\nnewFixedThreadPool   \n创建一个定长线程池，可控制线程最大并发数，超出的线程会在队列中等待。  \n\nnewScheduledThreadPool    \n创建一个定长线程池，支持定时及周期性任务执行。  \n\nnewSingleThreadExecutor  \n创建一个单线程化的线程池，它只会用唯一的工作线程来执行任务，保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。\n\n- newCachedThreadPool  \n\n创建一个可缓存线程池，如果线程池长度超过处理需要，可灵活回收空闲线程，若无可回收，则新建线程。示例代码如下：\n\n```java\npackage com.xuyiwei.my_03;\n\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\n\n/**\n * Created by xuyiwei on 2019/2/13.\n */\npublic class CacheTheadToolDemo {\n    public static void main(String[] args) {\n        ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();\n        for(int i=0;i<10;i++){\n            final int temp = i;\n            try {\n                Thread.sleep(1000);\n            } catch (InterruptedException e) {\n                e.printStackTrace();\n            }\n            //总结: 线程池为无限大，当执行第二个任务时第一个任务已经完成，会复用执行第一个任务的线程，而不用每次新建线程。\n            newCachedThreadPool.execute(new Runnable() {\n\n                @Override\n                public void run() {\n                    try {\n                        Thread.sleep(100);\n                    } catch (Exception e) {\n                        // TODO: handle exception\n                    }\n                    System.out.println(Thread.currentThread().getName() + \",i:\" + temp);\n\n                }\n            });\n\n        }\n    }\n    /**\n     * pool-1-thread-1,i:0\n     pool-1-thread-1,i:1\n     pool-1-thread-1,i:2\n     pool-1-thread-1,i:3\n     pool-1-thread-1,i:4\n     pool-1-thread-1,i:5\n     pool-1-thread-1,i:6\n     pool-1-thread-1,i:7\n     pool-1-thread-1,i:8\n     pool-1-thread-1,i:9\n     */\n}\n\n```\n\n- newFixedThreadPool  \n\n创建一个定长线程池，可控制线程最大并发数，超出的线程会在队列中等待。示例代码如下：\n```java\npackage com.xuyiwei.my_03;\n\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\n\n/**\n * Created by xuyiwei on 2019/2/13.\n */\npublic class FixedThreadPoolDemo {\n    public static void main(String[] args) {\n        ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(5);\n        for (int i = 0; i < 10; i++) {\n            final int temp = i;\n            newFixedThreadPool.execute(new Runnable() {\n\n                @Override\n                public void run() {\n                    System.out.println(Thread.currentThread().getId() + \",i:\" + temp);\n\n                }\n            });\n        }\n\n    }\n    /**\n     * 10,i:0\n     11,i:1\n     12,i:2\n     13,i:3\n     14,i:4\n     10,i:5\n     14,i:6\n     11,i:7\n     13,i:8\n     12,i:9\n\n     */\n}\n\n```\n总结:因为一共线程池的长度是5，因此该线程池最多只有5个线程可以同时执行。\n定长线程池的大小最好根据系统资源进行设置。如Runtime.getRuntime().availableProcessors()\n\n- newScheduledThreadPool\n\n创建一个定长线程池，支持定时及周期性任务执行。延迟执行示例代码如下：\n```java\npackage com.xuyiwei.my_03;\n\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.ScheduledExecutorService;\nimport java.util.concurrent.TimeUnit;\n\n/**\n * Created by xuyiwei on 2019/2/13.\n */\npublic class ScheduledThreadPoolDemo {\n    public static void main(String[] args) {\n        //表示延迟3秒执行。\n        ScheduledExecutorService newScheduledThreadPool = Executors.newScheduledThreadPool(5);\n        for (int i = 0; i < 10; i++) {\n            final int temp = i;\n            newScheduledThreadPool.schedule(new Runnable() {\n                public void run() {\n                    System.out.println(\"i:\" + temp);\n                }\n            }, 3, TimeUnit.SECONDS);\n        }\n\n    }\n}\n```\n\n- newSingleThreadExecutor\n\n创建一个单线程化的线程池，它只会用唯一的工作线程来执行任务，保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。示例代码如下：\n\n```java\npackage com.xuyiwei.my_03;\n\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\n\n/**\n * Created by xuyiwei on 2019/2/13.\n */\npublic class SingleThreadExecutorDemo {\n    public static void main(String[] args) {\n        ExecutorService newSingleThreadExecutor = Executors.newSingleThreadExecutor();\n        for (int i = 0; i < 10; i++) {\n            final int index = i;\n            newSingleThreadExecutor.execute(new Runnable() {\n                //注意: 结果依次输出，相当于顺序执行各个任务。\n                @Override\n                public void run() {\n                    System.out.println(\"index:\" + index);\n                    try {\n                        Thread.sleep(200);\n                    } catch (Exception e) {\n                        // TODO: handle exception\n                    }\n                }\n            });\n        }\n    }\n}\n```\n注意: 结果依次输出，相当于顺序执行各个任务。\n\n\n- 线程池原理剖析  \n\n提交一个任务到线程池中，线程池的处理流程如下：\n1、判断线程池里的核心线程是否都在执行任务，如果不是（核心线程空闲或者还有核心线程没有被创建）则创建一个新的工作线程来执行任务。如果核心线程都在执行任务，则进入下个流程。  \n2、线程池判断工作队列是否已满，如果工作队列没有满，则将新提交的任务存储在这个工作队列里。如果工作队列满了，则进入下个流程。  \n3、判断线程池里的线程是否都处于工作状态，如果没有，则创建一个新的工作线程来执行任务。如果已经满了，则交给饱和策略来处理这个任务。\n\n- 合理配置线程池\n\n要想合理的配置线程池，就必须首先分析任务特性，可以从以下几个角度来进行分析：   \n任务的性质：CPU密集型任务，IO密集型任务和混合型任务。  \n任务的优先级：高，中和低。  \n任务的执行时间：长，中和短。  \n任务的依赖性：是否依赖其他系统资源，如数据库连接。  \n任务性质不同的任务可以用不同规模的线程池分开处理。CPU密集型任务配置尽可能少的线程数量，如配置Ncpu+1个线程的线程池。IO密集型任务则由于需要等待IO操作，线程并不是一直在执行任务，则配置尽可能多的线程，如2*Ncpu。混合型的任务，如果可以拆分，则将其拆分成一个CPU密集型任务和一个IO密集型任务，只要这两个任务执行的时间相差不是太大，那么分解后执行的吞吐率要高于串行执行的吞吐率，如果这两个任务执行时间相差太大，则没必要进行分解。我们可以通过Runtime.getRuntime().availableProcessors()方法获得当前设备的CPU个数。  \n优先级不同的任务可以使用优先级队列PriorityBlockingQueue来处理。它可以让优先级高的任务先得到执行，需要注意的是如果一直有优先级高的任务提交到队列里，那么优先级低的任务可能永远不能执行。  \n执行时间不同的任务可以交给不同规模的线程池来处理，或者也可以使用优先级队列，让执行时间短的任务先执行。  \n依赖数据库连接池的任务，因为线程提交SQL后需要等待数据库返回结果，如果等待的时间越长CPU空闲时间就越长，那么线程数应该设置越大，这样才能更好的利用CPU。  \n \nCPU密集型时，任务可以少配置线程数，大概和机器的cpu核数相当，这样可以使得每个线程都在执行任务  \nIO密集型时，大部分线程都阻塞，故需要多配置线程数，2*cpu核数  \n操作系统之名称解释：  \n某些进程花费了绝大多数时间在计算上，而其他则在等待I/O上花费了大多是时间，前者称为计算密集型（CPU密集型）computer-bound，后者称为I/O密集型，I/O-bound。\n\n\n', b'0', '2', '1', b'0', b'0', 'java并发包&线程池原理分析&锁的深度化', 'java并发包&线程池原理分析&锁的深度化', b'1', '2019-02-14 03:33:52', '2019-02-14 03:33:52'), ('6', '多线程锁', '1', 'xblog/cover/20190214113430935_20190203151358162_timg.jpeg', null, b'1', '<ul>\n<li><h2 id=\"java-\">java锁机制</h2>\n</li>\n<li>悲观锁和乐观锁区别<br>场景<br>当多个请求同时操作数据库时，首先将订单状态改为已支付，在金额加上200，在同时并发场景查询条件下，会造成重复通知。<br>SQL:<br>Update </li>\n</ul>\n<p><img src=\"http://xyw-blog.oss-cn-qingdao.aliyuncs.com/xblog/article/20190214110835881_image-1550113714399.png?Expires=1865473716&amp;OSSAccessKeyId=LTAIgFRAJMcFAUPX&amp;Signature=fPxa%2BA2zmni4kVNCe7o8cOUPUIU%3D\" alt=\"file\"></p>\n<ul>\n<li><p>悲观锁与乐观锁<br>悲观锁:悲观锁悲观的认为每一次操作都会造成更新丢失问题，在每次查询时加上排他锁。<br>每次去拿数据的时候都认为别人会修改，所以每次在拿数据的时候都会上锁，这样别人想拿这个数据就会block直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制，比如行锁，表锁等，读锁，写锁等，都是在做操作之前先上锁。<br>Select * from xxx for update;<br>乐观锁:乐观锁会乐观的认为每次查询都不会造成更新丢失,利用版本字段控制  </p>\n</li>\n<li><p>重入锁  </p>\n</li>\n</ul>\n<p>锁作为并发共享数据，保证一致性的工具，在JAVA平台有多种实现(如 synchronized 和 ReentrantLock等等 ) 。这些已经写好提供的锁为我们开发提供了便利。<br>重入锁，也叫做递归锁，指的是同一线程 外层函数获得锁之后 ，内层递归函数仍然有获取该锁的代码，但不受影响。  </p>\n<pre><code class=\"lang-java\">package com.xuyiwei.my_03;\n\n/**\n * Created by xuyiwei on 2019/2/14.\n */\n\npublic class ChongruLock implements Runnable {\n    public  synchronized void get() {\n        System.out.println(&quot;name:&quot; + Thread.currentThread().getName() + &quot; get();&quot;);\n        set();\n    }\n\n    //同一个对象的锁是可以进行传递的 虽然这里不加synchronized关键字也是可以的\n    public synchronized  void set() {\n        System.out.println(&quot;name:&quot; + Thread.currentThread().getName() + &quot; set();&quot;);\n    }\n\n    @Override\n\n    public void run() {\n        get();\n    }\n\n    public static void main(String[] args) {\n        ChongruLock ss = new ChongruLock();\n        new Thread(ss).start();\n        new Thread(ss).start();\n        new Thread(ss).start();\n        new Thread(ss).start();\n    }\n}\n</code></pre>\n<pre><code class=\"lang-java\">package com.xuyiwei.my_03;\n\nimport java.util.concurrent.locks.ReentrantLock;\n\n/**\n * Created by xuyiwei on 2019/2/14.\n */\npublic class Lock2 extends Thread {\n    ReentrantLock lock = new ReentrantLock();\n    public void get() {\n        //lock本身就是重入锁机制 就是同一个对象的lock锁是可以传递的  并不会产生互斥然后死锁\n        lock.lock();\n        System.out.println(Thread.currentThread().getId());\n        set();\n        lock.unlock();\n    }\n    public void set() {\n        lock.lock();\n        System.out.println(Thread.currentThread().getId());\n        lock.unlock();\n    }\n    @Override\n    public void run() {\n        get();\n    }\n    public static void main(String[] args) {\n        Lock2 ss = new Lock2();\n        new Thread(ss).start();\n        new Thread(ss).start();\n        new Thread(ss).start();\n    }\n\n}\n</code></pre>\n<ul>\n<li>读写锁</li>\n</ul>\n<p>相比Java中的锁(Locks in Java)里Lock实现，读写锁更复杂一些。假设你的程序中涉及到对一些共享资源的读和写操作，且写操作没有读操作那么频繁。在没有写操作的时候，两个线程同时读一个资源没有任何问题，所以应该允许多个线程能在同时读取共享资源。但是如果有一个线程想去写这些共享资源，就不应该再有其它线程对该资源进行读或写（译者注：也就是说：读-读能共存，读-写不能共存，写-写不能共存）。这就需要一个读/写锁来解决这个问题。Java5在java.util.concurrent包中已经包含了读写锁。尽管如此，我们还是应该了解其实现背后的原理。</p>\n<pre><code class=\"lang-java\">package com.xuyiwei.my_03;\n\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.locks.Lock;\nimport java.util.concurrent.locks.ReentrantReadWriteLock;\n\n/**\n * Created by xuyiwei on 2019/2/14.\n */\npublic class Cache {\n    static ConcurrentHashMap&lt;String,String&gt; map = new ConcurrentHashMap&lt;&gt;();\n    static ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();\n    static Lock r = rwl.readLock();\n    static Lock w = rwl.writeLock();\n    public static void put(String key,String value){\n        w.lock();\n        System.out.println(&quot;开始写入key为&quot;+key+&quot;,value为&quot;+value+&quot;的数据&quot;);\n        map.put(key,value);\n        w.unlock();\n        System.out.println(&quot;写入结束key为&quot;+key+&quot;,value为&quot;+value+&quot;的数据&quot;);\n\n    }\n    public static void read(String key ){\n        w.lock();\n        System.out.println(&quot;开始读取key为&quot;+key+&quot;的数据&quot;);\n        String ValueExp = map.get(key);\n        w.unlock();\n        System.out.println(&quot;开始读取key为&quot;+key+&quot;的数据,value为&quot;+ValueExp+&quot;的数据&quot;);\n    }\n\n    public static void main(String[] args) {\n        new Thread(new Runnable() {\n            @Override\n            public void run() {\n                for (int i = 0; i &lt; 10; i++) {\n                    Cache.put(i + &quot;&quot;, i + &quot;&quot;);\n                }\n\n            }\n        }).start();\n        new Thread(new Runnable() {\n            @Override\n            public void run() {\n                for (int i = 0; i &lt; 10; i++) {\n                    Cache.read(i +&quot;&quot;);\n                }\n\n            }\n        }).start();\n    }\n    /**\n     开始写入key为0,value为0的数据\n     写入结束key为0,value为0的数据\n     开始写入key为1,value为1的数据\n     写入结束key为1,value为1的数据\n     开始写入key为2,value为2的数据\n     写入结束key为2,value为2的数据\n     开始写入key为3,value为3的数据\n     写入结束key为3,value为3的数据\n     开始写入key为4,value为4的数据\n     写入结束key为4,value为4的数据\n     开始写入key为5,value为5的数据\n     写入结束key为5,value为5的数据\n     开始写入key为6,value为6的数据\n     写入结束key为6,value为6的数据\n     开始写入key为7,value为7的数据\n     写入结束key为7,value为7的数据\n     开始写入key为8,value为8的数据\n     写入结束key为8,value为8的数据\n     开始写入key为9,value为9的数据\n     写入结束key为9,value为9的数据\n     开始读取key为0的数据\n     开始读取key为0的数据,value为0的数据\n     开始读取key为1的数据\n     开始读取key为1的数据,value为1的数据\n     开始读取key为2的数据\n     开始读取key为2的数据,value为2的数据\n     开始读取key为3的数据\n     开始读取key为3的数据,value为3的数据\n     开始读取key为4的数据\n     开始读取key为4的数据,value为4的数据\n     开始读取key为5的数据\n     开始读取key为5的数据,value为5的数据\n     开始读取key为6的数据\n     开始读取key为6的数据,value为6的数据\n     开始读取key为7的数据\n     开始读取key为7的数据,value为7的数据\n     开始读取key为8的数据\n     开始读取key为8的数据,value为8的数据\n     开始读取key为9的数据\n     开始读取key为9的数据,value为9的数据\n     */\n}\n</code></pre>\n<ul>\n<li>CAS无锁机制</li>\n</ul>\n<p>（1）与锁相比，使用比较交换（下文简称CAS）会使程序看起来更加复杂一些。但由于其非阻塞性，它对死锁问题天生免疫，并且，线程间的相互影响也远远比基于锁的方式要小。更为重要的是，使用无锁的方式完全没有锁竞争带来的系统开销，也没有线程间频繁调度带来的开销，因此，它要比基于锁的方式拥有更优越的性能。<br>（2）无锁的好处：<br>第一，在高并发的情况下，它比有锁的程序拥有更好的性能；<br>第二，它天生就是死锁免疫的。就凭借这两个优势，就值得我们冒险尝试使用无锁的并发。<br>（3）CAS算法的过程是这样：它包含三个参数CAS(V,E,N): V表示要更新的变量，E表示预期值，N表示新值。仅当V值等于E值时，才会将V的值设为N，如果V值和E值不同，则说明已经有其他线程做了更新，则当前线程什么都不做。最后，CAS返回当前V的真实值。<br>（4）CAS操作是抱着乐观的态度进行的，它总是认为自己可以成功完成操作。当多个线程同时使用CAS操作一个变量时，只有一个会胜出，并成功更新，其余均会失败。失败的线程不会被挂起，仅是被告知失败，并且允许再次尝试，当然也允许失败的线程放弃操作。基于这样的原理，CAS操作即使没有锁，也可以发现其他线程对当前线程的干扰，并进行恰当的处理。<br>（5）简单地说，CAS需要你额外给出一个期望值，也就是你认为这个变量现在应该是什么样子的。如果变量不是你想象的那样，那说明它已经被别人修改过了。你就重新读取，再次尝试修改就好了。<br>（6）在硬件层面，大部分的现代处理器都已经支持原子化的CAS指令。在JDK5.0以后，虚拟机便可以使用这个指令来实现并发操作和并发数据结构，并且，这种操作在虚拟机中可以说是无处不在。</p>\n<p><img src=\"http://xyw-blog.oss-cn-qingdao.aliyuncs.com/xblog/article/20190214112615805_image-1550114775010.png?Expires=1865474776&amp;OSSAccessKeyId=LTAIgFRAJMcFAUPX&amp;Signature=YIK31XZxrUT2g4pNvUwN2vNTyxU%3D\" alt=\"file\"></p>\n<pre><code class=\"lang-java\">\n    /**\n     * Atomically increments by one the current value.\n     *\n     * @return the updated value\n     */\n    public final int incrementAndGet() {\n        for (;;) {\n            //获取当前值\n            int current = get();\n            //设置期望值\n            int next = current + 1;\n            //调用Native方法compareAndSet，执行CAS操作\n            if (compareAndSet(current, next))\n                //成功后才会返回期望值，否则无线循环\n                return next;\n        }\n    }\n</code></pre>\n<ul>\n<li>自旋锁  </li>\n</ul>\n<p>当一个线程 调用这个不可重入的自旋锁去加锁的时候没问题，当再次调用lock()的时候，因为自旋锁的持有引用已经不为空了，该线程对象会误认为是别人的线程持有了自旋锁使用了CAS原子操作，lock函数将owner设置为当前线程，并且预测原来的值为空。unlock函数将owner设置为null，并且预测值为当前线程。<br>当有第二个线程调用lock操作时由于owner值不为空，导致循环一直被执行，直至第一个线程调用unlock函数将owner设置为null，第二个线程才能进入临界区。<br>由于自旋锁只是将当前线程不停地执行循环体，不进行线程状态的改变，所以响应速度更快。但当线程数不停增加时，性能下降明显，因为每个线程都需要执行，占用CPU时间。如果线程竞争不激烈，并且保持锁的时间段。适合使用自旋锁。</p>\n<ul>\n<li>分步式锁</li>\n</ul>\n<p>如果想在不同的jvm中保证数据同步，使用分布式锁技术。<br>有数据库实现、缓存实现、Zookeeper分布式锁</p>\n', '- ## java锁机制\n- 悲观锁和乐观锁区别  \n场景  \n当多个请求同时操作数据库时，首先将订单状态改为已支付，在金额加上200，在同时并发场景查询条件下，会造成重复通知。\nSQL:\nUpdate \n\n![file](http://xyw-blog.oss-cn-qingdao.aliyuncs.com/xblog/article/20190214110835881_image-1550113714399.png?Expires=1865473716&OSSAccessKeyId=LTAIgFRAJMcFAUPX&Signature=fPxa%2BA2zmni4kVNCe7o8cOUPUIU%3D)\n\n- 悲观锁与乐观锁\n悲观锁:悲观锁悲观的认为每一次操作都会造成更新丢失问题，在每次查询时加上排他锁。  \n每次去拿数据的时候都认为别人会修改，所以每次在拿数据的时候都会上锁，这样别人想拿这个数据就会block直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制，比如行锁，表锁等，读锁，写锁等，都是在做操作之前先上锁。  \nSelect * from xxx for update;  \n乐观锁:乐观锁会乐观的认为每次查询都不会造成更新丢失,利用版本字段控制  \n\n- 重入锁  \n\n锁作为并发共享数据，保证一致性的工具，在JAVA平台有多种实现(如 synchronized 和 ReentrantLock等等 ) 。这些已经写好提供的锁为我们开发提供了便利。\n重入锁，也叫做递归锁，指的是同一线程 外层函数获得锁之后 ，内层递归函数仍然有获取该锁的代码，但不受影响。  \n```java\npackage com.xuyiwei.my_03;\n\n/**\n * Created by xuyiwei on 2019/2/14.\n */\n\npublic class ChongruLock implements Runnable {\n    public  synchronized void get() {\n        System.out.println(\"name:\" + Thread.currentThread().getName() + \" get();\");\n        set();\n    }\n\n    //同一个对象的锁是可以进行传递的 虽然这里不加synchronized关键字也是可以的\n    public synchronized  void set() {\n        System.out.println(\"name:\" + Thread.currentThread().getName() + \" set();\");\n    }\n\n    @Override\n\n    public void run() {\n        get();\n    }\n\n    public static void main(String[] args) {\n        ChongruLock ss = new ChongruLock();\n        new Thread(ss).start();\n        new Thread(ss).start();\n        new Thread(ss).start();\n        new Thread(ss).start();\n    }\n}\n```\n\n```java\npackage com.xuyiwei.my_03;\n\nimport java.util.concurrent.locks.ReentrantLock;\n\n/**\n * Created by xuyiwei on 2019/2/14.\n */\npublic class Lock2 extends Thread {\n    ReentrantLock lock = new ReentrantLock();\n    public void get() {\n        //lock本身就是重入锁机制 就是同一个对象的lock锁是可以传递的  并不会产生互斥然后死锁\n        lock.lock();\n        System.out.println(Thread.currentThread().getId());\n        set();\n        lock.unlock();\n    }\n    public void set() {\n        lock.lock();\n        System.out.println(Thread.currentThread().getId());\n        lock.unlock();\n    }\n    @Override\n    public void run() {\n        get();\n    }\n    public static void main(String[] args) {\n        Lock2 ss = new Lock2();\n        new Thread(ss).start();\n        new Thread(ss).start();\n        new Thread(ss).start();\n    }\n\n}\n```\n\n\n- 读写锁\n\n相比Java中的锁(Locks in Java)里Lock实现，读写锁更复杂一些。假设你的程序中涉及到对一些共享资源的读和写操作，且写操作没有读操作那么频繁。在没有写操作的时候，两个线程同时读一个资源没有任何问题，所以应该允许多个线程能在同时读取共享资源。但是如果有一个线程想去写这些共享资源，就不应该再有其它线程对该资源进行读或写（译者注：也就是说：读-读能共存，读-写不能共存，写-写不能共存）。这就需要一个读/写锁来解决这个问题。Java5在java.util.concurrent包中已经包含了读写锁。尽管如此，我们还是应该了解其实现背后的原理。\n```java\npackage com.xuyiwei.my_03;\n\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.locks.Lock;\nimport java.util.concurrent.locks.ReentrantReadWriteLock;\n\n/**\n * Created by xuyiwei on 2019/2/14.\n */\npublic class Cache {\n    static ConcurrentHashMap<String,String> map = new ConcurrentHashMap<>();\n    static ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();\n    static Lock r = rwl.readLock();\n    static Lock w = rwl.writeLock();\n    public static void put(String key,String value){\n        w.lock();\n        System.out.println(\"开始写入key为\"+key+\",value为\"+value+\"的数据\");\n        map.put(key,value);\n        w.unlock();\n        System.out.println(\"写入结束key为\"+key+\",value为\"+value+\"的数据\");\n\n    }\n    public static void read(String key ){\n        w.lock();\n        System.out.println(\"开始读取key为\"+key+\"的数据\");\n        String ValueExp = map.get(key);\n        w.unlock();\n        System.out.println(\"开始读取key为\"+key+\"的数据,value为\"+ValueExp+\"的数据\");\n    }\n\n    public static void main(String[] args) {\n        new Thread(new Runnable() {\n            @Override\n            public void run() {\n                for (int i = 0; i < 10; i++) {\n                    Cache.put(i + \"\", i + \"\");\n                }\n\n            }\n        }).start();\n        new Thread(new Runnable() {\n            @Override\n            public void run() {\n                for (int i = 0; i < 10; i++) {\n                    Cache.read(i +\"\");\n                }\n\n            }\n        }).start();\n    }\n    /**\n     开始写入key为0,value为0的数据\n     写入结束key为0,value为0的数据\n     开始写入key为1,value为1的数据\n     写入结束key为1,value为1的数据\n     开始写入key为2,value为2的数据\n     写入结束key为2,value为2的数据\n     开始写入key为3,value为3的数据\n     写入结束key为3,value为3的数据\n     开始写入key为4,value为4的数据\n     写入结束key为4,value为4的数据\n     开始写入key为5,value为5的数据\n     写入结束key为5,value为5的数据\n     开始写入key为6,value为6的数据\n     写入结束key为6,value为6的数据\n     开始写入key为7,value为7的数据\n     写入结束key为7,value为7的数据\n     开始写入key为8,value为8的数据\n     写入结束key为8,value为8的数据\n     开始写入key为9,value为9的数据\n     写入结束key为9,value为9的数据\n     开始读取key为0的数据\n     开始读取key为0的数据,value为0的数据\n     开始读取key为1的数据\n     开始读取key为1的数据,value为1的数据\n     开始读取key为2的数据\n     开始读取key为2的数据,value为2的数据\n     开始读取key为3的数据\n     开始读取key为3的数据,value为3的数据\n     开始读取key为4的数据\n     开始读取key为4的数据,value为4的数据\n     开始读取key为5的数据\n     开始读取key为5的数据,value为5的数据\n     开始读取key为6的数据\n     开始读取key为6的数据,value为6的数据\n     开始读取key为7的数据\n     开始读取key为7的数据,value为7的数据\n     开始读取key为8的数据\n     开始读取key为8的数据,value为8的数据\n     开始读取key为9的数据\n     开始读取key为9的数据,value为9的数据\n     */\n}\n\n```\n\n- CAS无锁机制\n\n（1）与锁相比，使用比较交换（下文简称CAS）会使程序看起来更加复杂一些。但由于其非阻塞性，它对死锁问题天生免疫，并且，线程间的相互影响也远远比基于锁的方式要小。更为重要的是，使用无锁的方式完全没有锁竞争带来的系统开销，也没有线程间频繁调度带来的开销，因此，它要比基于锁的方式拥有更优越的性能。  \n（2）无锁的好处：  \n第一，在高并发的情况下，它比有锁的程序拥有更好的性能；  \n第二，它天生就是死锁免疫的。就凭借这两个优势，就值得我们冒险尝试使用无锁的并发。   \n（3）CAS算法的过程是这样：它包含三个参数CAS(V,E,N): V表示要更新的变量，E表示预期值，N表示新值。仅当V值等于E值时，才会将V的值设为N，如果V值和E值不同，则说明已经有其他线程做了更新，则当前线程什么都不做。最后，CAS返回当前V的真实值。   \n（4）CAS操作是抱着乐观的态度进行的，它总是认为自己可以成功完成操作。当多个线程同时使用CAS操作一个变量时，只有一个会胜出，并成功更新，其余均会失败。失败的线程不会被挂起，仅是被告知失败，并且允许再次尝试，当然也允许失败的线程放弃操作。基于这样的原理，CAS操作即使没有锁，也可以发现其他线程对当前线程的干扰，并进行恰当的处理。  \n（5）简单地说，CAS需要你额外给出一个期望值，也就是你认为这个变量现在应该是什么样子的。如果变量不是你想象的那样，那说明它已经被别人修改过了。你就重新读取，再次尝试修改就好了。  \n（6）在硬件层面，大部分的现代处理器都已经支持原子化的CAS指令。在JDK5.0以后，虚拟机便可以使用这个指令来实现并发操作和并发数据结构，并且，这种操作在虚拟机中可以说是无处不在。\n\n![file](http://xyw-blog.oss-cn-qingdao.aliyuncs.com/xblog/article/20190214112615805_image-1550114775010.png?Expires=1865474776&OSSAccessKeyId=LTAIgFRAJMcFAUPX&Signature=YIK31XZxrUT2g4pNvUwN2vNTyxU%3D)\n\n```java\n\n    /**\n     * Atomically increments by one the current value.\n     *\n     * @return the updated value\n     */\n    public final int incrementAndGet() {\n        for (;;) {\n            //获取当前值\n            int current = get();\n            //设置期望值\n            int next = current + 1;\n            //调用Native方法compareAndSet，执行CAS操作\n            if (compareAndSet(current, next))\n                //成功后才会返回期望值，否则无线循环\n                return next;\n        }\n    }\n    \n```\n\n- 自旋锁  \n\n当一个线程 调用这个不可重入的自旋锁去加锁的时候没问题，当再次调用lock()的时候，因为自旋锁的持有引用已经不为空了，该线程对象会误认为是别人的线程持有了自旋锁使用了CAS原子操作，lock函数将owner设置为当前线程，并且预测原来的值为空。unlock函数将owner设置为null，并且预测值为当前线程。  \n当有第二个线程调用lock操作时由于owner值不为空，导致循环一直被执行，直至第一个线程调用unlock函数将owner设置为null，第二个线程才能进入临界区。    \n由于自旋锁只是将当前线程不停地执行循环体，不进行线程状态的改变，所以响应速度更快。但当线程数不停增加时，性能下降明显，因为每个线程都需要执行，占用CPU时间。如果线程竞争不激烈，并且保持锁的时间段。适合使用自旋锁。\n\n- 分步式锁\n\n如果想在不同的jvm中保证数据同步，使用分布式锁技术。\n有数据库实现、缓存实现、Zookeeper分布式锁\n', b'0', '2', '1', b'0', b'0', '多线程锁', '多线程锁 java锁 ', b'1', '2019-02-14 03:34:46', '2019-02-14 03:34:46');
INSERT INTO `biz_article` VALUES ('7', '2019-2-13随笔', '1', 'xblog/cover/20190214113712840_20190213133014515_b79a2572eccb402a863cadce51ee20bc.jpg', null, b'1', '<p>哈哈，昨天的随笔又忘记写了，今天随便糊弄糊弄，其实很多寂寞无聊都是自己找的，只要给自己定好目标就能过好每一天。昨晚健身房打开第二天，体重也成功到了160以下。希望今天继续保持，然后也希望今天在睡觉前把随笔写了。大家中午快乐</p>\n', '哈哈，昨天的随笔又忘记写了，今天随便糊弄糊弄，其实很多寂寞无聊都是自己找的，只要给自己定好目标就能过好每一天。昨晚健身房打开第二天，体重也成功到了160以下。希望今天继续保持，然后也希望今天在睡觉前把随笔写了。大家中午快乐', b'0', '3', '1', b'0', b'1', '随笔', '随笔', b'0', '2019-02-14 03:37:23', '2019-02-14 03:37:23'), ('8', 'Java并发编程：线程池的使用', '1', 'xblog/cover/20190215144744110_20190203151358162_timg.jpeg', null, b'1', '<p>我们使用线程的时候就去创建一个线程，这样实现起来非常简便，但是就会有一个问题：</p>\n<p>　　如果并发的线程数量很多，并且每个线程都是执行一个时间很短的任务就结束了，这样频繁创建线程就会大大降低系统的效率，因为频繁创建线程和销毁线程需要时间。</p>\n<p>　　那么有没有一种办法使得线程可以复用，就是执行完一个任务，并不被销毁，而是可以继续执行其他的任务？</p>\n<p>　　在Java中可以通过线程池来达到这样的效果。今天我们就来详细讲解一下Java的线程池，首先我们从最核心的ThreadPoolExecutor类中的方法讲起，然后再讲述它的实现原理，接着给出了它的使用示例，最后讨论了一下如何合理配置线程池的大小。</p>\n<p>　　以下是本文的目录大纲：</p>\n<p>　　一.Java中的ThreadPoolExecutor类</p>\n<p>　　二.深入剖析线程池实现原理</p>\n<p>　　三.使用示例</p>\n<p>　　四.如何合理配置线程池的大小　</p>\n<p>　　若有不正之处请多多谅解，并欢迎批评指正。</p>\n<p>　　请尊重作者劳动成果，转载请标明原文链接：</p>\n<p>　　<a href=\"http://www.cnblogs.com/dolphin0520/p/3932921.html\">http://www.cnblogs.com/dolphin0520/p/3932921.html</a> \n　　</p>\n<ul>\n<li><h2 id=\"java-threadpoolexecutor-\">Java中的ThreadPoolExecutor类</h2>\n</li>\n</ul>\n<p>　　java.uitl.concurrent.ThreadPoolExecutor类是线程池中最核心的一个类，因此如果要透彻地了解Java中的线程池，必须先了解这个类。下面我们来看一下ThreadPoolExecutor类的具体实现源码。<br>　　在ThreadPoolExecutor类中提供了四个构造方法：  \n　　\n　　\n　</p>\n<pre><code class=\"lang-java\">　public class ThreadPoolExecutor extends AbstractExecutorService {\n    .....\n    public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,\n            BlockingQueue&lt;Runnable&gt; workQueue);\n\n    public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,\n            BlockingQueue&lt;Runnable&gt; workQueue,ThreadFactory threadFactory);\n\n    public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,\n            BlockingQueue&lt;Runnable&gt; workQueue,RejectedExecutionHandler handler);\n\n    public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,\n        BlockingQueue&lt;Runnable&gt; workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler);\n    ...\n}\n</code></pre>\n<p>　从上面的代码可以得知，ThreadPoolExecutor继承了AbstractExecutorService类，并提供了四个构造器，事实上，通过观察每个构造器的源码具体实现，发现前面三个构造器都是调用的第四个构造器进行的初始化工作。</p>\n<p> 　　下面解释下一下构造器中各个参数的含义：</p>\n<blockquote>\n<p>corePoolSize：核心池的大小，这个参数跟后面讲述的线程池的实现原理有非常大的关系。在创建了线程池后，默认情况下，线程池中并没有任何线程，而是等待有任务到来才创建线程去执行任务，除非调用了prestartAllCoreThreads()或者prestartCoreThread()方法，从这2个方法的名字就可以看出，是预创建线程的意思，即在没有任务到来之前就创建corePoolSize个线程或者一个线程。默认情况下，在创建了线程池后，线程池中的线程数为0，当有任务来之后，就会创建一个线程去执行任务，当线程池中的线程数目达到corePoolSize后，就会把到达的任务放到缓存队列当中；  </p>\n<p>maximumPoolSize：线程池最大线程数，这个参数也是一个非常重要的参数，它表示在线程池中最多能创建多少个线程；  </p>\n<p>keepAliveTime：表示线程没有任务执行时最多保持多久时间会终止。默认情况下，只有当线程池中的线程数大于corePoolSize时，keepAliveTime才会起作用，直到线程池中的线程数不大于corePoolSize，即当线程池中的线程数大于corePoolSize时，如果一个线程空闲的时间达到keepAliveTime，则会终止，直到线程池中的线程数不超过corePoolSize。但是如果调用了allowCoreThreadTimeOut(boolean)方法，在线程池中的线程数不大于corePoolSize时，keepAliveTime参数也会起作用，直到线程池中的线程数为0；  </p>\n<p>unit：参数keepAliveTime的时间单位，有7种取值，在TimeUnit类中有7种静态属性：</p>\n</blockquote>\n<pre><code class=\"lang-java\">TimeUnit.DAYS;               //天\nTimeUnit.HOURS;             //小时\nTimeUnit.MINUTES;           //分钟\nTimeUnit.SECONDS;           //秒\nTimeUnit.MILLISECONDS;      //毫秒\nTimeUnit.MICROSECONDS;      //微妙\nTimeUnit.NANOSECONDS;       //纳秒\n</code></pre>\n<blockquote>\n<p>workQueue：一个阻塞队列，用来存储等待执行的任务，这个参数的选择也很重要，会对线程池的运行过程产生重大影响，一般来说，这里的阻塞队列有以下几种选择：</p>\n</blockquote>\n<pre><code class=\"lang-java\">ArrayBlockingQueue;\nLinkedBlockingQueue;\nSynchronousQueue;\n</code></pre>\n<p>　ArrayBlockingQueue和PriorityBlockingQueue使用较少，一般使用LinkedBlockingQueue和Synchronous。线程池的排队策略与BlockingQueue有关。 \n　</p>\n<blockquote>\n<p>threadFactory：线程工厂，主要用来创建线程；  </p>\n<p>handler：表示当拒绝处理任务时的策略，有以下四种取值：</p>\n</blockquote>\n<pre><code class=\"lang-java\">ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。 \nThreadPoolExecutor.DiscardPolicy：也是丢弃任务，但是不抛出异常。 \nThreadPoolExecutor.DiscardOldestPolicy：丢弃队列最前面的任务，然后重新尝试执行任务（重复此过程）\nThreadPoolExecutor.CallerRunsPolicy：由调用线程处理该任务\n</code></pre>\n<p>　　具体参数的配置与线程池的关系将在下一节讲述。</p>\n<p>　　从上面给出的ThreadPoolExecutor类的代码可以知道，ThreadPoolExecutor继承了AbstractExecutorService，我们来看一下AbstractExecutorService的实现：\n　　</p>\n<pre><code class=\"lang-java\">public abstract class AbstractExecutorService implements ExecutorService {\n\n\n    protected &lt;T&gt; RunnableFuture&lt;T&gt; newTaskFor(Runnable runnable, T value) { };\n    protected &lt;T&gt; RunnableFuture&lt;T&gt; newTaskFor(Callable&lt;T&gt; callable) { };\n    public Future&lt;?&gt; submit(Runnable task) {};\n    public &lt;T&gt; Future&lt;T&gt; submit(Runnable task, T result) { };\n    public &lt;T&gt; Future&lt;T&gt; submit(Callable&lt;T&gt; task) { };\n    private &lt;T&gt; T doInvokeAny(Collection&lt;? extends Callable&lt;T&gt;&gt; tasks,\n                            boolean timed, long nanos)\n        throws InterruptedException, ExecutionException, TimeoutException {\n    };\n    public &lt;T&gt; T invokeAny(Collection&lt;? extends Callable&lt;T&gt;&gt; tasks)\n        throws InterruptedException, ExecutionException {\n    };\n    public &lt;T&gt; T invokeAny(Collection&lt;? extends Callable&lt;T&gt;&gt; tasks,\n                           long timeout, TimeUnit unit)\n        throws InterruptedException, ExecutionException, TimeoutException {\n    };\n    public &lt;T&gt; List&lt;Future&lt;T&gt;&gt; invokeAll(Collection&lt;? extends Callable&lt;T&gt;&gt; tasks)\n        throws InterruptedException {\n    };\n    public &lt;T&gt; List&lt;Future&lt;T&gt;&gt; invokeAll(Collection&lt;? extends Callable&lt;T&gt;&gt; tasks,\n                                         long timeout, TimeUnit unit)\n        throws InterruptedException {\n    };\n}\n</code></pre>\n<p>　AbstractExecutorService是一个抽象类，它实现了ExecutorService接口。</p>\n<p>　　我们接着看ExecutorService接口的实现：</p>\n<pre><code class=\"lang-java\">public interface ExecutorService extends Executor {\n\n    void shutdown();\n    boolean isShutdown();\n    boolean isTerminated();\n    boolean awaitTermination(long timeout, TimeUnit unit)\n        throws InterruptedException;\n    &lt;T&gt; Future&lt;T&gt; submit(Callable&lt;T&gt; task);\n    &lt;T&gt; Future&lt;T&gt; submit(Runnable task, T result);\n    Future&lt;?&gt; submit(Runnable task);\n    &lt;T&gt; List&lt;Future&lt;T&gt;&gt; invokeAll(Collection&lt;? extends Callable&lt;T&gt;&gt; tasks)\n        throws InterruptedException;\n    &lt;T&gt; List&lt;Future&lt;T&gt;&gt; invokeAll(Collection&lt;? extends Callable&lt;T&gt;&gt; tasks,\n                                  long timeout, TimeUnit unit)\n        throws InterruptedException;\n\n    &lt;T&gt; T invokeAny(Collection&lt;? extends Callable&lt;T&gt;&gt; tasks)\n        throws InterruptedException, ExecutionException;\n    &lt;T&gt; T invokeAny(Collection&lt;? extends Callable&lt;T&gt;&gt; tasks,\n                    long timeout, TimeUnit unit)\n        throws InterruptedException, ExecutionException, TimeoutException;\n}\n`\n</code></pre>\n<p> 　　而ExecutorService又是继承了Executor接口，我们看一下Executor接口的实现：</p>\n<pre><code class=\"lang-java\">public interface Executor {\n    void execute(Runnable command);\n}\n</code></pre>\n<p> 　　到这里，大家应该明白了ThreadPoolExecutor、AbstractExecutorService、ExecutorService和Executor几个之间的关系了。</p>\n<p>　　Executor是一个顶层接口，在它里面只声明了一个方法execute(Runnable)，返回值为void，参数为Runnable类型，从字面意思可以理解，就是用来执行传进去的任务的；</p>\n<p>　　然后ExecutorService接口继承了Executor接口，并声明了一些方法：submit、invokeAll、invokeAny以及shutDown等；</p>\n<p>　　抽象类AbstractExecutorService实现了ExecutorService接口，基本实现了ExecutorService中声明的所有方法；</p>\n<p>　　然后ThreadPoolExecutor继承了类AbstractExecutorService。</p>\n<p>　　在ThreadPoolExecutor类中有几个非常重要的方法：\n　　</p>\n<pre><code class=\"lang-java\">execute()\nsubmit()\nshutdown()\nshutdownNow()\n</code></pre>\n<blockquote>\n<p>　execute()方法实际上是Executor中声明的方法，在ThreadPoolExecutor进行了具体的实现，这个方法是ThreadPoolExecutor的核心方法，通过这个方法可以向线程池提交一个任务，交由线程池去执行。</p>\n<p>　　submit()方法是在ExecutorService中声明的方法，在AbstractExecutorService就已经有了具体的实现，在ThreadPoolExecutor中并没有对其进行重写，这个方法也是用来向线程池提交任务的，但是它和execute()方法不同，它能够返回任务执行的结果，去看submit()方法的实现，会发现它实际上还是调用的execute()方法，只不过它利用了Future来获取任务执行结果（Future相关内容将在下一篇讲述）。</p>\n<p>　　shutdown()和shutdownNow()是用来关闭线程池的。</p>\n</blockquote>\n<p>　　还有很多其他的方法：</p>\n<p>　　比如：getQueue() 、getPoolSize() 、getActiveCount()、getCompletedTaskCount()等获取与线程池相关属性的方法，有兴趣的朋友可以自行查阅API。\n　　</p>\n<ul>\n<li><h2 id=\"-\">深入剖析线程池实现原理</h2>\n</li>\n</ul>\n<p>　　在上一节我们从宏观上介绍了ThreadPoolExecutor，下面我们来深入解析一下线程池的具体实现原理，将从下面几个方面讲解：</p>\n<ul>\n<li><p>　1.线程池状态</p>\n</li>\n<li><p>　　2.任务的执行</p>\n</li>\n<li><p>　　3.线程池中的线程初始化</p>\n</li>\n<li><p>　　4.任务缓存队列及排队策略</p>\n</li>\n<li><p>　　5.任务拒绝策略</p>\n</li>\n<li><p>　　6.线程池的关闭</p>\n</li>\n<li><p>　　7.线程池容量的动态调整</p>\n</li>\n</ul>\n<p>1.线程池状态</p>\n<p>　　在ThreadPoolExecutor中定义了一个volatile变量，另外定义了几个static final变量表示线程池的各个状态：\n　　</p>\n<pre><code class=\"lang-java\">volatile int runState;\nstatic final int RUNNING    = 0;\nstatic final int SHUTDOWN   = 1;\nstatic final int STOP       = 2;\nstatic final int TERMINATED = 3;\n</code></pre>\n<p>runState表示当前线程池的状态，它是一个volatile变量用来保证线程之间的可见性；</p>\n<p>　　下面的几个static final变量表示runState可能的几个取值。</p>\n<p>　　当创建线程池后，初始时，线程池处于RUNNING状态；</p>\n<p>　　如果调用了shutdown()方法，则线程池处于SHUTDOWN状态，此时线程池不能够接受新的任务，它会等待所有任务执行完毕；</p>\n<p>　　如果调用了shutdownNow()方法，则线程池处于STOP状态，此时线程池不能接受新的任务，并且会去尝试终止正在执行的任务；</p>\n<p>　　当线程池处于SHUTDOWN或STOP状态，并且所有工作线程已经销毁，任务缓存队列已经清空或执行结束后，线程池被设置为TERMINATED状态。</p>\n<p>2.任务的执行</p>\n<p>　　在了解将任务提交给线程池到任务执行完毕整个过程之前，我们先来看一下ThreadPoolExecutor类中其他的一些比较重要成员变量：\n　　</p>\n<pre><code class=\"lang-java\">private final BlockingQueue&lt;Runnable&gt; workQueue;              //任务缓存队列，用来存放等待执行的任务\nprivate final ReentrantLock mainLock = new ReentrantLock();   //线程池的主要状态锁，对线程池状态（比如线程池大小\n                                                              //、runState等）的改变都要使用这个锁\nprivate final HashSet&lt;Worker&gt; workers = new HashSet&lt;Worker&gt;();  //用来存放工作集\n\nprivate volatile long  keepAliveTime;    //线程存货时间   \nprivate volatile boolean allowCoreThreadTimeOut;   //是否允许为核心线程设置存活时间\nprivate volatile int   corePoolSize;     //核心池的大小（即线程池中的线程数目大于这个参数时，提交的任务会被放进任务缓存队列）\nprivate volatile int   maximumPoolSize;   //线程池最大能容忍的线程数\n\nprivate volatile int   poolSize;       //线程池中当前的线程数\n\nprivate volatile RejectedExecutionHandler handler; //任务拒绝策略\n\nprivate volatile ThreadFactory threadFactory;   //线程工厂，用来创建线程\n\nprivate int largestPoolSize;   //用来记录线程池中曾经出现过的最大线程数\n\nprivate long completedTaskCount;   //用来记录已经执行完毕的任务个数\n</code></pre>\n<p>每个变量的作用都已经标明出来了，这里要重点解释一下corePoolSize、maximumPoolSize、largestPoolSize三个变量。</p>\n<p>　　corePoolSize在很多地方被翻译成核心池大小，其实我的理解这个就是线程池的大小。举个简单的例子：</p>\n<p>　　假如有一个工厂，工厂里面有10个工人，每个工人同时只能做一件任务。</p>\n<p>　　因此只要当10个工人中有工人是空闲的，来了任务就分配给空闲的工人做；</p>\n<p>　　当10个工人都有任务在做时，如果还来了任务，就把任务进行排队等待；</p>\n<p>　　如果说新任务数目增长的速度远远大于工人做任务的速度，那么此时工厂主管可能会想补救措施，比如重新招4个临时工人进来；</p>\n<p>　　然后就将任务也分配给这4个临时工人做；</p>\n<p>　　如果说着14个工人做任务的速度还是不够，此时工厂主管可能就要考虑不再接收新的任务或者抛弃前面的一些任务了。</p>\n<p>　　当这14个工人当中有人空闲时，而新任务增长的速度又比较缓慢，工厂主管可能就考虑辞掉4个临时工了，只保持原来的10个工人，毕竟请额外的工人是要花钱的。</p>\n<p>　　这个例子中的corePoolSize就是10，而maximumPoolSize就是14（10+4）。</p>\n<p>　　也就是说corePoolSize就是线程池大小，maximumPoolSize在我看来是线程池的一种补救措施，即任务量突然过大时的一种补救措施。</p>\n<p>　　不过为了方便理解，在本文后面还是将corePoolSize翻译成核心池大小。</p>\n<p>　　largestPoolSize只是一个用来起记录作用的变量，用来记录线程池中曾经有过的最大线程数目，跟线程池的容量没有任何关系。</p>\n<p>　　下面我们进入正题，看一下任务从提交到最终执行完毕经历了哪些过程。</p>\n<p>　　在ThreadPoolExecutor类中，最核心的任务提交方法是execute()方法，虽然通过submit也可以提交任务，但是实际上submit方法里面最终调用的还是execute()方法，所以我们只需要研究execute()方法的实现原理即可：\n　　</p>\n<pre><code class=\"lang-java\">public void execute(Runnable command) {\n    if (command == null)\n        throw new NullPointerException();\n    if (poolSize &gt;= corePoolSize || !addIfUnderCorePoolSize(command)) {\n        if (runState == RUNNING &amp;&amp; workQueue.offer(command)) {\n            if (runState != RUNNING || poolSize == 0)\n                ensureQueuedTaskHandled(command);\n        }\n        else if (!addIfUnderMaximumPoolSize(command))\n            reject(command); // is shutdown or saturated\n    }\n}\n</code></pre>\n<p>上面的代码可能看起来不是那么容易理解，下面我们一句一句解释：</p>\n<p>　　首先，判断提交的任务command是否为null，若是null，则抛出空指针异常；</p>\n<p>　　接着是这句，这句要好好理解一下：</p>\n<pre><code class=\"lang-java\">if (poolSize &gt;= corePoolSize || !addIfUnderCorePoolSize(command))\n</code></pre>\n<p>由于是或条件运算符，所以先计算前半部分的值，如果线程池中当前线程数不小于核心池大小，那么就会直接进入下面的if语句块了。</p>\n<p>　　如果线程池中当前线程数小于核心池大小，则接着执行后半部分，也就是执行\n　　</p>\n<pre><code class=\"lang-java\">addIfUnderCorePoolSize(command)\n</code></pre>\n<p>　如果执行完addIfUnderCorePoolSize这个方法返回false，则继续执行下面的if语句块，否则整个方法就直接执行完毕了。</p>\n<p>　　如果执行完addIfUnderCorePoolSize这个方法返回false，然后接着判断：</p>\n<pre><code class=\"lang-java\">if (runState == RUNNING &amp;&amp; workQueue.offer(command))\n</code></pre>\n<p> 　　如果当前线程池处于RUNNING状态，则将任务放入任务缓存队列；如果当前线程池不处于RUNNING状态或者任务放入缓存队列失败，则执行：</p>\n<pre><code class=\"lang-java\">addIfUnderMaximumPoolSize(command)\n</code></pre>\n<p>　如果执行addIfUnderMaximumPoolSize方法失败，则执行reject()方法进行任务拒绝处理。</p>\n<p>　　回到前面：\n　　\n　　</p>\n<pre><code class=\"lang-java\">if (runState == RUNNING &amp;&amp; workQueue.offer(command))\n</code></pre>\n<p> 　　这句的执行，如果说当前线程池处于RUNNING状态且将任务放入任务缓存队列成功，则继续进行判断：</p>\n<pre><code class=\"lang-java\">if (runState != RUNNING || poolSize == 0)\n</code></pre>\n<p> 　　这句判断是为了防止在将此任务添加进任务缓存队列的同时其他线程突然调用shutdown或者shutdownNow方法关闭了线程池的一种应急措施。如果是这样就执行：</p>\n<pre><code class=\"lang-java\">ensureQueuedTaskHandled(command)\n</code></pre>\n<p>进行应急处理，从名字可以看出是保证 添加到任务缓存队列中的任务得到处理。</p>\n<p>　　我们接着看2个关键方法的实现：addIfUnderCorePoolSize和addIfUnderMaximumPoolSize：\n　　</p>\n<pre><code class=\"lang-java\">private boolean addIfUnderCorePoolSize(Runnable firstTask) {\n    Thread t = null;\n    final ReentrantLock mainLock = this.mainLock;\n    mainLock.lock();\n    try {\n        if (poolSize &lt; corePoolSize &amp;&amp; runState == RUNNING)\n            t = addThread(firstTask);        //创建线程去执行firstTask任务   \n        } finally {\n        mainLock.unlock();\n    }\n    if (t == null)\n        return false;\n    t.start();\n    return true;\n}\n</code></pre>\n<p>这个是addIfUnderCorePoolSize方法的具体实现，从名字可以看出它的意图就是当低于核心吃大小时执行的方法。下面看其具体实现，首先获取到锁，因为这地方涉及到线程池状态的变化，先通过if语句判断当前线程池中的线程数目是否小于核心池大小，有朋友也许会有疑问：前面在execute()方法中不是已经判断过了吗，只有线程池当前线程数目小于核心池大小才会执行addIfUnderCorePoolSize方法的，为何这地方还要继续判断？原因很简单，前面的判断过程中并没有加锁，因此可能在execute方法判断的时候poolSize小于corePoolSize，而判断完之后，在其他线程中又向线程池提交了任务，就可能导致poolSize不小于corePoolSize了，所以需要在这个地方继续判断。然后接着判断线程池的状态是否为RUNNING，原因也很简单，因为有可能在其他线程中调用了shutdown或者shutdownNow方法。然后就是执行</p>\n<pre><code class=\"lang-java\">t = addThread(firstTask);\n</code></pre>\n<p>这个方法也非常关键，传进去的参数为提交的任务，返回值为Thread类型。然后接着在下面判断t是否为空，为空则表明创建线程失败（即poolSize&gt;=corePoolSize或者runState不等于RUNNING），否则调用t.start()方法启动线程。</p>\n<p>　　我们来看一下addThread方法的实现：\n　　</p>\n<pre><code class=\"lang-java\">private Thread addThread(Runnable firstTask) {\n    Worker w = new Worker(firstTask);\n    Thread t = threadFactory.newThread(w);  //创建一个线程，执行任务   \n    if (t != null) {\n        w.thread = t;            //将创建的线程的引用赋值为w的成员变量       \n        workers.add(w);\n        int nt = ++poolSize;     //当前线程数加1       \n        if (nt &gt; largestPoolSize)\n            largestPoolSize = nt;\n    }\n    return t;\n}\n</code></pre>\n<p>　　在addThread方法中，首先用提交的任务创建了一个Worker对象，然后调用线程工厂threadFactory创建了一个新的线程t，然后将线程t的引用赋值给了Worker对象的成员变量thread，接着通过workers.add(w)将Worker对象添加到工作集当中。</p>\n<p>　　下面我们看一下Worker类的实现：\n　　</p>\n<pre><code class=\"lang-java\">private final class Worker implements Runnable {\n    private final ReentrantLock runLock = new ReentrantLock();\n    private Runnable firstTask;\n    volatile long completedTasks;\n    Thread thread;\n    Worker(Runnable firstTask) {\n        this.firstTask = firstTask;\n    }\n    boolean isActive() {\n        return runLock.isLocked();\n    }\n    void interruptIfIdle() {\n        final ReentrantLock runLock = this.runLock;\n        if (runLock.tryLock()) {\n            try {\n        if (thread != Thread.currentThread())\n        thread.interrupt();\n            } finally {\n                runLock.unlock();\n            }\n        }\n    }\n    void interruptNow() {\n        thread.interrupt();\n    }\n\n    private void runTask(Runnable task) {\n        final ReentrantLock runLock = this.runLock;\n        runLock.lock();\n        try {\n            if (runState &lt; STOP &amp;&amp;\n                Thread.interrupted() &amp;&amp;\n                runState &gt;= STOP)\n            boolean ran = false;\n            beforeExecute(thread, task);   //beforeExecute方法是ThreadPoolExecutor类的一个方法，没有具体实现，用户可以根据\n            //自己需要重载这个方法和后面的afterExecute方法来进行一些统计信息，比如某个任务的执行时间等           \n            try {\n                task.run();\n                ran = true;\n                afterExecute(task, null);\n                ++completedTasks;\n            } catch (RuntimeException ex) {\n                if (!ran)\n                    afterExecute(task, ex);\n                throw ex;\n            }\n        } finally {\n            runLock.unlock();\n        }\n    }\n\n    public void run() {\n        try {\n            Runnable task = firstTask;\n            firstTask = null;\n            while (task != null || (task = getTask()) != null) {\n                runTask(task);\n                task = null;\n            }\n        } finally {\n            workerDone(this);   //当任务队列中没有任务时，进行清理工作       \n        }\n    }\n}\n</code></pre>\n<p>它实际上实现了Runnable接口，因此上面的Thread t = threadFactory.newThread(w);效果跟下面这句的效果基本一样：</p>\n<pre><code class=\"lang-java\">Thread t = new Thread(w);\n</code></pre>\n<p>　相当于传进去了一个Runnable任务，在线程t中执行这个Runnable。</p>\n<p>　　既然Worker实现了Runnable接口，那么自然最核心的方法便是run()方法了：</p>\n<pre><code class=\"lang-java\">public void run() {\n    try {\n        Runnable task = firstTask;\n        firstTask = null;\n        while (task != null || (task = getTask()) != null) {\n            runTask(task);\n            task = null;\n        }\n    } finally {\n        workerDone(this);\n    }\n}\n</code></pre>\n<p>　从run方法的实现可以看出，它首先执行的是通过构造器传进来的任务firstTask，在调用runTask()执行完firstTask之后，在while循环里面不断通过getTask()去取新的任务来执行，那么去哪里取呢？自然是从任务缓存队列里面去取，getTask是ThreadPoolExecutor类中的方法，并不是Worker类中的方法，下面是getTask方法的实现：\n　</p>\n<pre><code class=\"lang-java\">Runnable getTask() {\n    for (;;) {\n        try {\n            int state = runState;\n            if (state &gt; SHUTDOWN)\n                return null;\n            Runnable r;\n            if (state == SHUTDOWN)  // Help drain queue\n                r = workQueue.poll();\n            else if (poolSize &gt; corePoolSize || allowCoreThreadTimeOut) //如果线程数大于核心池大小或者允许为核心池线程设置空闲时间，\n                //则通过poll取任务，若等待一定的时间取不到任务，则返回null\n                r = workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS);\n            else\n                r = workQueue.take();\n            if (r != null)\n                return r;\n            if (workerCanExit()) {    //如果没取到任务，即r为null，则判断当前的worker是否可以退出\n                if (runState &gt;= SHUTDOWN) // Wake up others\n                    interruptIdleWorkers();   //中断处于空闲状态的worker\n                return null;\n            }\n            // Else retry\n        } catch (InterruptedException ie) {\n            // On interruption, re-check runState\n        }\n    }\n}\n</code></pre>\n<p>　在getTask中，先判断当前线程池状态，如果runState大于SHUTDOWN（即为STOP或者TERMINATED），则直接返回null。</p>\n<p>　　如果runState为SHUTDOWN或者RUNNING，则从任务缓存队列取任务。</p>\n<p>　　如果当前线程池的线程数大于核心池大小corePoolSize或者允许为核心池中的线程设置空闲存活时间，则调用poll(time,timeUnit)来取任务，这个方法会等待一定的时间，如果取不到任务就返回null。</p>\n<p>　　然后判断取到的任务r是否为null，为null则通过调用workerCanExit()方法来判断当前worker是否可以退出，我们看一下workerCanExit()的实现：\n　　</p>\n<pre><code class=\"lang-java\">private boolean workerCanExit() {\n    final ReentrantLock mainLock = this.mainLock;\n    mainLock.lock();\n    boolean canExit;\n    //如果runState大于等于STOP，或者任务缓存队列为空了\n    //或者  允许为核心池线程设置空闲存活时间并且线程池中的线程数目大于1\n    try {\n        canExit = runState &gt;= STOP ||\n            workQueue.isEmpty() ||\n            (allowCoreThreadTimeOut &amp;&amp;\n             poolSize &gt; Math.max(1, corePoolSize));\n    } finally {\n        mainLock.unlock();\n    }\n    return canExit;\n}\n</code></pre>\n<p>　也就是说如果线程池处于STOP状态、或者任务队列已为空或者允许为核心池线程设置空闲存活时间并且线程数大于1时，允许worker退出。如果允许worker退出，则调用interruptIdleWorkers()中断处于空闲状态的worker，我们看一下interruptIdleWorkers()的实现：\n　</p>\n<pre><code class=\"lang-java\">void interruptIdleWorkers() {\n    final ReentrantLock mainLock = this.mainLock;\n    mainLock.lock();\n    try {\n        for (Worker w : workers)  //实际上调用的是worker的interruptIfIdle()方法\n            w.interruptIfIdle();\n    } finally {\n        mainLock.unlock();\n    }\n}\n</code></pre>\n<p> 　　从实现可以看出，它实际上调用的是worker的interruptIfIdle()方法，在worker的interruptIfIdle()方法中：</p>\n<pre><code class=\"lang-java\">void interruptIfIdle() {\n    final ReentrantLock runLock = this.runLock;\n    if (runLock.tryLock()) {    //注意这里，是调用tryLock()来获取锁的，因为如果当前worker正在执行任务，锁已经被获取了，是无法获取到锁的\n                                //如果成功获取了锁，说明当前worker处于空闲状态\n        try {\n    if (thread != Thread.currentThread())  \n    thread.interrupt();\n        } finally {\n            runLock.unlock();\n        }\n    }\n}\n</code></pre>\n<p>  　　这里有一个非常巧妙的设计方式，假如我们来设计线程池，可能会有一个任务分派线程，当发现有线程空闲时，就从任务缓存队列中取一个任务交给空闲线程执行。但是在这里，并没有采用这样的方式，因为这样会要额外地对任务分派线程进行管理，无形地会增加难度和复杂度，这里直接让执行完任务的线程去任务缓存队列里面取任务来执行。</p>\n<p> 　　我们再看addIfUnderMaximumPoolSize方法的实现，这个方法的实现思想和addIfUnderCorePoolSize方法的实现思想非常相似，唯一的区别在于addIfUnderMaximumPoolSize方法是在线程池中的线程数达到了核心池大小并且往任务队列中添加任务失败的情况下执行的：\n 　　</p>\n<pre><code class=\"lang-java\">private boolean addIfUnderMaximumPoolSize(Runnable firstTask) {\n    Thread t = null;\n    final ReentrantLock mainLock = this.mainLock;\n    mainLock.lock();\n    try {\n        if (poolSize &lt; maximumPoolSize &amp;&amp; runState == RUNNING)\n            t = addThread(firstTask);\n    } finally {\n        mainLock.unlock();\n    }\n    if (t == null)\n        return false;\n    t.start();\n    return true;\n}\n</code></pre>\n<p>　看到没有，其实它和addIfUnderCorePoolSize方法的实现基本一模一样，只是if语句判断条件中的poolSize &lt; maximumPoolSize不同而已。</p>\n<p>　　到这里，大部分朋友应该对任务提交给线程池之后到被执行的整个过程有了一个基本的了解，下面总结一下：</p>\n<p>　　1）首先，要清楚corePoolSize和maximumPoolSize的含义；</p>\n<p>　　2）其次，要知道Worker是用来起到什么作用的；</p>\n<p>　　3）要知道任务提交给线程池之后的处理策略，这里总结一下主要有4点：</p>\n<p>如果当前线程池中的线程数目小于corePoolSize，则每来一个任务，就会创建一个线程去执行这个任务；  </p>\n<p>如果当前线程池中的线程数目&gt;=corePoolSize，则每来一个任务，会尝试将其添加到任务缓存队列当中，若添加成功，则该任务会等待空闲线程将其取出去执行；若添加失败（一般来说是任务缓存队列已满），则会尝试创建新的线程去执行这个任务；  </p>\n<p>如果当前线程池中的线程数目达到maximumPoolSize，则会采取任务拒绝策略进行处理；  </p>\n<p>如果线程池中的线程数量大于 corePoolSize时，如果某线程空闲时间超过keepAliveTime，线程将被终止，直至线程池中的线程数目不大于corePoolSize；如果允许为核心池中的线程设置存活时间，那么核心池中的线程空闲时间超过keepAliveTime，线程也会被终止。  </p>\n<p>3.线程池中的线程初始化</p>\n<p>　　默认情况下，创建线程池之后，线程池中是没有线程的，需要提交任务之后才会创建线程。</p>\n<p>　　在实际中如果需要线程池创建之后立即创建线程，可以通过以下两个方法办到：</p>\n<p>prestartCoreThread()：初始化一个核心线程；<br>prestartAllCoreThreads()：初始化所有核心线程<br>　　下面是这2个方法的实现：</p>\n<pre><code class=\"lang-java\">public boolean prestartCoreThread() {\n    return addIfUnderCorePoolSize(null); //注意传进去的参数是null\n}\n\npublic int prestartAllCoreThreads() {\n    int n = 0;\n    while (addIfUnderCorePoolSize(null))//注意传进去的参数是null\n        ++n;\n    return n;\n}\n</code></pre>\n<p>注意上面传进去的参数是null，根据第2小节的分析可知如果传进去的参数为null，则最后执行线程会阻塞在getTask方法中的</p>\n<pre><code class=\"lang-java\">r = workQueue.take();\n</code></pre>\n<p> 　　即等待任务队列中有任务。</p>\n<p>4.任务缓存队列及排队策略</p>\n<p>在前面我们多次提到了任务缓存队列，即workQueue，它用来存放等待执行的任务。</p>\n<p>　　workQueue的类型为BlockingQueue<Runnable>，通常可以取下面三种类型：</p>\n<p>　　1）ArrayBlockingQueue：基于数组的先进先出队列，此队列创建时必须指定大小；</p>\n<p>　　2）LinkedBlockingQueue：基于链表的先进先出队列，如果创建时没有指定此队列大小，则默认为Integer.MAX_VALUE；</p>\n<p>　　3）synchronousQueue：这个队列比较特殊，它不会保存提交的任务，而是将直接新建一个线程来执行新来的任务。</p>\n<p>5.任务拒绝策略</p>\n<p>　　当线程池的任务缓存队列已满并且线程池中的线程数目达到maximumPoolSize，如果还有任务到来就会采取任务拒绝策略，通常有以下四种策略：\n　　</p>\n<pre><code class=\"lang-java\">ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。\nThreadPoolExecutor.DiscardPolicy：也是丢弃任务，但是不抛出异常。\nThreadPoolExecutor.DiscardOldestPolicy：丢弃队列最前面的任务，然后重新尝试执行任务（重复此过程）\nThreadPoolExecutor.CallerRunsPolicy：由调用线程处理该任务\n</code></pre>\n<p>6.线程池的关闭<br>　ThreadPoolExecutor提供了两个方法，用于线程池的关闭，分别是shutdown()和shutdownNow()，其中：</p>\n<blockquote>\n<p>shutdown()：不会立即终止线程池，而是要等所有任务缓存队列中的任务都执行完后才终止，但再也不会接受新的任务  </p>\n<p>shutdownNow()：立即终止线程池，并尝试打断正在执行的任务，并且清空任务缓存队列，返回尚未执行的任务</p>\n</blockquote>\n<p>7.线程池容量的动态调整<br>　　ThreadPoolExecutor提供了动态调整线程池容量大小的方法：setCorePoolSize()和setMaximumPoolSize()，</p>\n<blockquote>\n<p>setCorePoolSize：设置核心池大小  </p>\n<p>setMaximumPoolSize：设置线程池最大能创建的线程数目大小  </p>\n</blockquote>\n<p>　　当上述参数从小变大时，ThreadPoolExecutor进行线程赋值，还可能立即创建新的线程来执行任务。\n　　\n　　</p>\n<ul>\n<li><h2 id=\"-\">三.使用示例</h2>\n</li>\n</ul>\n<p>　前面我们讨论了关于线程池的实现原理，这一节我们来看一下它的具体使用：\n　</p>\n<pre><code class=\"lang-java\">　public class Test {\n     public static void main(String[] args) {   \n         ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 200, TimeUnit.MILLISECONDS,\n                 new ArrayBlockingQueue&lt;Runnable&gt;(5));\n\n         for(int i=0;i&lt;15;i++){\n             MyTask myTask = new MyTask(i);\n             executor.execute(myTask);\n             System.out.println(&quot;线程池中线程数目：&quot;+executor.getPoolSize()+&quot;，队列中等待执行的任务数目：&quot;+\n             executor.getQueue().size()+&quot;，已执行玩别的任务数目：&quot;+executor.getCompletedTaskCount());\n         }\n         executor.shutdown();\n     }\n}\n\n\nclass MyTask implements Runnable {\n    private int taskNum;\n\n    public MyTask(int num) {\n        this.taskNum = num;\n    }\n\n    @Override\n    public void run() {\n        System.out.println(&quot;正在执行task &quot;+taskNum);\n        try {\n            Thread.currentThread().sleep(4000);\n        } catch (InterruptedException e) {\n            e.printStackTrace();\n        }\n        System.out.println(&quot;task &quot;+taskNum+&quot;执行完毕&quot;);\n    }\n}\n</code></pre>\n<p>　<br>执行结果：</p>\n<pre><code class=\"lang-java\">正在执行task 0\n线程池中线程数目：1，队列中等待执行的任务数目：0，已执行玩别的任务数目：0\n线程池中线程数目：2，队列中等待执行的任务数目：0，已执行玩别的任务数目：0\n正在执行task 1\n线程池中线程数目：3，队列中等待执行的任务数目：0，已执行玩别的任务数目：0\n正在执行task 2\n线程池中线程数目：4，队列中等待执行的任务数目：0，已执行玩别的任务数目：0\n正在执行task 3\n线程池中线程数目：5，队列中等待执行的任务数目：0，已执行玩别的任务数目：0\n正在执行task 4\n线程池中线程数目：5，队列中等待执行的任务数目：1，已执行玩别的任务数目：0\n线程池中线程数目：5，队列中等待执行的任务数目：2，已执行玩别的任务数目：0\n线程池中线程数目：5，队列中等待执行的任务数目：3，已执行玩别的任务数目：0\n线程池中线程数目：5，队列中等待执行的任务数目：4，已执行玩别的任务数目：0\n线程池中线程数目：5，队列中等待执行的任务数目：5，已执行玩别的任务数目：0\n线程池中线程数目：6，队列中等待执行的任务数目：5，已执行玩别的任务数目：0\n正在执行task 10\n线程池中线程数目：7，队列中等待执行的任务数目：5，已执行玩别的任务数目：0\n正在执行task 11\n线程池中线程数目：8，队列中等待执行的任务数目：5，已执行玩别的任务数目：0\n正在执行task 12\n线程池中线程数目：9，队列中等待执行的任务数目：5，已执行玩别的任务数目：0\n正在执行task 13\n线程池中线程数目：10，队列中等待执行的任务数目：5，已执行玩别的任务数目：0\n正在执行task 14\ntask 3执行完毕\ntask 0执行完毕\ntask 2执行完毕\ntask 1执行完毕\n正在执行task 8\n正在执行task 7\n正在执行task 6\n正在执行task 5\ntask 4执行完毕\ntask 10执行完毕\ntask 11执行完毕\ntask 13执行完毕\ntask 12执行完毕\n正在执行task 9\ntask 14执行完毕\ntask 8执行完毕\ntask 5执行完毕\ntask 7执行完毕\ntask 6执行完毕\ntask 9执行完毕\n</code></pre>\n<p>从执行结果可以看出，当线程池中线程的数目大于5时，便将任务放入任务缓存队列里面，当任务缓存队列满了之后，便创建新的线程。如果上面程序中，将for循环中改成执行20个任务，就会抛出任务拒绝异常了。</p>\n<p>　　不过在java doc中，并不提倡我们直接使用ThreadPoolExecutor，而是使用Executors类中提供的几个静态方法来创建线程池：\n　</p>\n<pre><code class=\"lang-java\">xecutors.newCachedThreadPool();        //创建一个缓冲池，缓冲池容量大小为Integer.MAX_VALUE\nExecutors.newSingleThreadExecutor();   //创建容量为1的缓冲池\nExecutors.newFixedThreadPool(int);    //创建固定容量大小的缓冲池\n</code></pre>\n<p>下面是这三个静态方法的具体实现;</p>\n<pre><code class=\"lang-java\">public static ExecutorService newFixedThreadPool(int nThreads) {\n    return new ThreadPoolExecutor(nThreads, nThreads,\n                                  0L, TimeUnit.MILLISECONDS,\n                                  new LinkedBlockingQueue&lt;Runnable&gt;());\n}\npublic static ExecutorService newSingleThreadExecutor() {\n    return new FinalizableDelegatedExecutorService\n        (new ThreadPoolExecutor(1, 1,\n                                0L, TimeUnit.MILLISECONDS,\n                                new LinkedBlockingQueue&lt;Runnable&gt;()));\n}\npublic static ExecutorService newCachedThreadPool() {\n    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,\n                                  60L, TimeUnit.SECONDS,\n                                  new SynchronousQueue&lt;Runnable&gt;());\n}\n</code></pre>\n<p>从它们的具体实现来看，它们实际上也是调用了ThreadPoolExecutor，只不过参数都已配置好了。</p>\n<p>　　newFixedThreadPool创建的线程池corePoolSize和maximumPoolSize值是相等的，它使用的LinkedBlockingQueue；</p>\n<p>　　newSingleThreadExecutor将corePoolSize和maximumPoolSize都设置为1，也使用的LinkedBlockingQueue；</p>\n<p>　　newCachedThreadPool将corePoolSize设置为0，将maximumPoolSize设置为Integer.MAX_VALUE，使用的SynchronousQueue，也就是说来了任务就创建线程运行，当线程空闲超过60秒，就销毁线程。</p>\n<p>　　实际中，如果Executors提供的三个静态方法能满足要求，就尽量使用它提供的三个方法，因为自己去手动配置ThreadPoolExecutor的参数有点麻烦，要根据实际任务的类型和数量来进行配置。</p>\n<p>　　另外，如果ThreadPoolExecutor达不到要求，可以自己继承ThreadPoolExecutor类进行重写。\n　　</p>\n<ul>\n<li><h2 id=\"-\">四.如何合理配置线程池的大小</h2>\n</li>\n</ul>\n<p>本节来讨论一个比较重要的话题：如何合理配置线程池大小，仅供参考。</p>\n<p>　　一般需要根据任务的类型来配置线程池大小：</p>\n<p>　　如果是CPU密集型任务，就需要尽量压榨CPU，参考值可以设为 NCPU+1</p>\n<p>　　如果是IO密集型任务，参考值可以设置为2*NCPU</p>\n<p>　　当然，这只是一个参考值，具体的设置还需要根据实际情况进行调整，比如可以先将线程池大小设置为参考值，再观察任务运行情况和系统负载、资源利用率来进行适当调整。</p>\n<p>　　参考资料：</p>\n<p>　　<a href=\"http://ifeve.com/java-threadpool/\">http://ifeve.com/java-threadpool/</a></p>\n<p>　　<a href=\"http://blog.163.com/among_1985/blog/static/275005232012618849266/\">http://blog.163.com/among_1985/blog/static/275005232012618849266/</a></p>\n<p>　　<a href=\"http://developer.51cto.com/art/201203/321885.htm\">http://developer.51cto.com/art/201203/321885.htm</a></p>\n<p>　　<a href=\"http://blog.csdn.net/java2000_wl/article/details/22097059\">http://blog.csdn.net/java2000_wl/article/details/22097059</a></p>\n<p>　　<a href=\"http://blog.csdn.net/cutesource/article/details/6061229\">http://blog.csdn.net/cutesource/article/details/6061229</a></p>\n<p>　　<a href=\"http://blog.csdn.net/xieyuooo/article/details/8718741\">http://blog.csdn.net/xieyuooo/article/details/8718741</a></p>\n', '我们使用线程的时候就去创建一个线程，这样实现起来非常简便，但是就会有一个问题：\n\n　　如果并发的线程数量很多，并且每个线程都是执行一个时间很短的任务就结束了，这样频繁创建线程就会大大降低系统的效率，因为频繁创建线程和销毁线程需要时间。\n\n　　那么有没有一种办法使得线程可以复用，就是执行完一个任务，并不被销毁，而是可以继续执行其他的任务？\n\n　　在Java中可以通过线程池来达到这样的效果。今天我们就来详细讲解一下Java的线程池，首先我们从最核心的ThreadPoolExecutor类中的方法讲起，然后再讲述它的实现原理，接着给出了它的使用示例，最后讨论了一下如何合理配置线程池的大小。\n\n　　以下是本文的目录大纲：\n\n　　一.Java中的ThreadPoolExecutor类\n\n　　二.深入剖析线程池实现原理\n\n　　三.使用示例\n\n　　四.如何合理配置线程池的大小　\n\n　　若有不正之处请多多谅解，并欢迎批评指正。\n\n　　请尊重作者劳动成果，转载请标明原文链接：\n\n　　http://www.cnblogs.com/dolphin0520/p/3932921.html \n　　\n- ## Java中的ThreadPoolExecutor类 \n\n　　java.uitl.concurrent.ThreadPoolExecutor类是线程池中最核心的一个类，因此如果要透彻地了解Java中的线程池，必须先了解这个类。下面我们来看一下ThreadPoolExecutor类的具体实现源码。\n　　在ThreadPoolExecutor类中提供了四个构造方法：  \n　　\n　　\n　\n```java\n　public class ThreadPoolExecutor extends AbstractExecutorService {\n    .....\n    public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,\n            BlockingQueue<Runnable> workQueue);\n \n    public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,\n            BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory);\n \n    public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,\n            BlockingQueue<Runnable> workQueue,RejectedExecutionHandler handler);\n \n    public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,\n        BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler);\n    ...\n}\n```\n\n　从上面的代码可以得知，ThreadPoolExecutor继承了AbstractExecutorService类，并提供了四个构造器，事实上，通过观察每个构造器的源码具体实现，发现前面三个构造器都是调用的第四个构造器进行的初始化工作。\n\n 　　下面解释下一下构造器中各个参数的含义：\n\n> corePoolSize：核心池的大小，这个参数跟后面讲述的线程池的实现原理有非常大的关系。在创建了线程池后，默认情况下，线程池中并没有任何线程，而是等待有任务到来才创建线程去执行任务，除非调用了prestartAllCoreThreads()或者prestartCoreThread()方法，从这2个方法的名字就可以看出，是预创建线程的意思，即在没有任务到来之前就创建corePoolSize个线程或者一个线程。默认情况下，在创建了线程池后，线程池中的线程数为0，当有任务来之后，就会创建一个线程去执行任务，当线程池中的线程数目达到corePoolSize后，就会把到达的任务放到缓存队列当中；  \n\n> maximumPoolSize：线程池最大线程数，这个参数也是一个非常重要的参数，它表示在线程池中最多能创建多少个线程；  \n\n> keepAliveTime：表示线程没有任务执行时最多保持多久时间会终止。默认情况下，只有当线程池中的线程数大于corePoolSize时，keepAliveTime才会起作用，直到线程池中的线程数不大于corePoolSize，即当线程池中的线程数大于corePoolSize时，如果一个线程空闲的时间达到keepAliveTime，则会终止，直到线程池中的线程数不超过corePoolSize。但是如果调用了allowCoreThreadTimeOut(boolean)方法，在线程池中的线程数不大于corePoolSize时，keepAliveTime参数也会起作用，直到线程池中的线程数为0；  \n\n> unit：参数keepAliveTime的时间单位，有7种取值，在TimeUnit类中有7种静态属性：\n\n```java\nTimeUnit.DAYS;               //天\nTimeUnit.HOURS;             //小时\nTimeUnit.MINUTES;           //分钟\nTimeUnit.SECONDS;           //秒\nTimeUnit.MILLISECONDS;      //毫秒\nTimeUnit.MICROSECONDS;      //微妙\nTimeUnit.NANOSECONDS;       //纳秒\n```\n\n> workQueue：一个阻塞队列，用来存储等待执行的任务，这个参数的选择也很重要，会对线程池的运行过程产生重大影响，一般来说，这里的阻塞队列有以下几种选择：\n\n```java\nArrayBlockingQueue;\nLinkedBlockingQueue;\nSynchronousQueue;\n\n```\n\n　ArrayBlockingQueue和PriorityBlockingQueue使用较少，一般使用LinkedBlockingQueue和Synchronous。线程池的排队策略与BlockingQueue有关。 \n　\n> threadFactory：线程工厂，主要用来创建线程；  \n\n> handler：表示当拒绝处理任务时的策略，有以下四种取值：\n\n```java\nThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。 \nThreadPoolExecutor.DiscardPolicy：也是丢弃任务，但是不抛出异常。 \nThreadPoolExecutor.DiscardOldestPolicy：丢弃队列最前面的任务，然后重新尝试执行任务（重复此过程）\nThreadPoolExecutor.CallerRunsPolicy：由调用线程处理该任务 \n```\n\n　　具体参数的配置与线程池的关系将在下一节讲述。\n\n　　从上面给出的ThreadPoolExecutor类的代码可以知道，ThreadPoolExecutor继承了AbstractExecutorService，我们来看一下AbstractExecutorService的实现：\n　　\n```java\npublic abstract class AbstractExecutorService implements ExecutorService {\n \n     \n    protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) { };\n    protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) { };\n    public Future<?> submit(Runnable task) {};\n    public <T> Future<T> submit(Runnable task, T result) { };\n    public <T> Future<T> submit(Callable<T> task) { };\n    private <T> T doInvokeAny(Collection<? extends Callable<T>> tasks,\n                            boolean timed, long nanos)\n        throws InterruptedException, ExecutionException, TimeoutException {\n    };\n    public <T> T invokeAny(Collection<? extends Callable<T>> tasks)\n        throws InterruptedException, ExecutionException {\n    };\n    public <T> T invokeAny(Collection<? extends Callable<T>> tasks,\n                           long timeout, TimeUnit unit)\n        throws InterruptedException, ExecutionException, TimeoutException {\n    };\n    public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)\n        throws InterruptedException {\n    };\n    public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,\n                                         long timeout, TimeUnit unit)\n        throws InterruptedException {\n    };\n}\n```\n\n　AbstractExecutorService是一个抽象类，它实现了ExecutorService接口。\n\n　　我们接着看ExecutorService接口的实现：\n\n\n```java\npublic interface ExecutorService extends Executor {\n \n    void shutdown();\n    boolean isShutdown();\n    boolean isTerminated();\n    boolean awaitTermination(long timeout, TimeUnit unit)\n        throws InterruptedException;\n    <T> Future<T> submit(Callable<T> task);\n    <T> Future<T> submit(Runnable task, T result);\n    Future<?> submit(Runnable task);\n    <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)\n        throws InterruptedException;\n    <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,\n                                  long timeout, TimeUnit unit)\n        throws InterruptedException;\n \n    <T> T invokeAny(Collection<? extends Callable<T>> tasks)\n        throws InterruptedException, ExecutionException;\n    <T> T invokeAny(Collection<? extends Callable<T>> tasks,\n                    long timeout, TimeUnit unit)\n        throws InterruptedException, ExecutionException, TimeoutException;\n}\n````\n\n 　　而ExecutorService又是继承了Executor接口，我们看一下Executor接口的实现：\n\n```java\npublic interface Executor {\n    void execute(Runnable command);\n}\n```\n\n 　　到这里，大家应该明白了ThreadPoolExecutor、AbstractExecutorService、ExecutorService和Executor几个之间的关系了。\n\n　　Executor是一个顶层接口，在它里面只声明了一个方法execute(Runnable)，返回值为void，参数为Runnable类型，从字面意思可以理解，就是用来执行传进去的任务的；\n\n　　然后ExecutorService接口继承了Executor接口，并声明了一些方法：submit、invokeAll、invokeAny以及shutDown等；\n\n　　抽象类AbstractExecutorService实现了ExecutorService接口，基本实现了ExecutorService中声明的所有方法；\n\n　　然后ThreadPoolExecutor继承了类AbstractExecutorService。\n\n　　在ThreadPoolExecutor类中有几个非常重要的方法：\n　　\n```java\nexecute()\nsubmit()\nshutdown()\nshutdownNow()\n```\n\n>　execute()方法实际上是Executor中声明的方法，在ThreadPoolExecutor进行了具体的实现，这个方法是ThreadPoolExecutor的核心方法，通过这个方法可以向线程池提交一个任务，交由线程池去执行。\n\n>　　submit()方法是在ExecutorService中声明的方法，在AbstractExecutorService就已经有了具体的实现，在ThreadPoolExecutor中并没有对其进行重写，这个方法也是用来向线程池提交任务的，但是它和execute()方法不同，它能够返回任务执行的结果，去看submit()方法的实现，会发现它实际上还是调用的execute()方法，只不过它利用了Future来获取任务执行结果（Future相关内容将在下一篇讲述）。\n\n>　　shutdown()和shutdownNow()是用来关闭线程池的。\n\n　　还有很多其他的方法：\n\n　　比如：getQueue() 、getPoolSize() 、getActiveCount()、getCompletedTaskCount()等获取与线程池相关属性的方法，有兴趣的朋友可以自行查阅API。\n　　\n- ## 深入剖析线程池实现原理\n\n　　在上一节我们从宏观上介绍了ThreadPoolExecutor，下面我们来深入解析一下线程池的具体实现原理，将从下面几个方面讲解：\n\n- 　1.线程池状态\n\n- 　　2.任务的执行\n\n- 　　3.线程池中的线程初始化\n\n- 　　4.任务缓存队列及排队策略\n\n- 　　5.任务拒绝策略\n\n- 　　6.线程池的关闭\n\n- 　　7.线程池容量的动态调整\n\n \n\n1.线程池状态\n\n　　在ThreadPoolExecutor中定义了一个volatile变量，另外定义了几个static final变量表示线程池的各个状态：\n　　\n```java\nvolatile int runState;\nstatic final int RUNNING    = 0;\nstatic final int SHUTDOWN   = 1;\nstatic final int STOP       = 2;\nstatic final int TERMINATED = 3;\n\n```\n\nrunState表示当前线程池的状态，它是一个volatile变量用来保证线程之间的可见性；\n\n　　下面的几个static final变量表示runState可能的几个取值。\n\n　　当创建线程池后，初始时，线程池处于RUNNING状态；\n\n　　如果调用了shutdown()方法，则线程池处于SHUTDOWN状态，此时线程池不能够接受新的任务，它会等待所有任务执行完毕；\n\n　　如果调用了shutdownNow()方法，则线程池处于STOP状态，此时线程池不能接受新的任务，并且会去尝试终止正在执行的任务；\n\n　　当线程池处于SHUTDOWN或STOP状态，并且所有工作线程已经销毁，任务缓存队列已经清空或执行结束后，线程池被设置为TERMINATED状态。\n\n\n2.任务的执行\n\n　　在了解将任务提交给线程池到任务执行完毕整个过程之前，我们先来看一下ThreadPoolExecutor类中其他的一些比较重要成员变量：\n　　\n```java\nprivate final BlockingQueue<Runnable> workQueue;              //任务缓存队列，用来存放等待执行的任务\nprivate final ReentrantLock mainLock = new ReentrantLock();   //线程池的主要状态锁，对线程池状态（比如线程池大小\n                                                              //、runState等）的改变都要使用这个锁\nprivate final HashSet<Worker> workers = new HashSet<Worker>();  //用来存放工作集\n \nprivate volatile long  keepAliveTime;    //线程存货时间   \nprivate volatile boolean allowCoreThreadTimeOut;   //是否允许为核心线程设置存活时间\nprivate volatile int   corePoolSize;     //核心池的大小（即线程池中的线程数目大于这个参数时，提交的任务会被放进任务缓存队列）\nprivate volatile int   maximumPoolSize;   //线程池最大能容忍的线程数\n \nprivate volatile int   poolSize;       //线程池中当前的线程数\n \nprivate volatile RejectedExecutionHandler handler; //任务拒绝策略\n \nprivate volatile ThreadFactory threadFactory;   //线程工厂，用来创建线程\n \nprivate int largestPoolSize;   //用来记录线程池中曾经出现过的最大线程数\n \nprivate long completedTaskCount;   //用来记录已经执行完毕的任务个数\n```\n\n\n每个变量的作用都已经标明出来了，这里要重点解释一下corePoolSize、maximumPoolSize、largestPoolSize三个变量。\n\n　　corePoolSize在很多地方被翻译成核心池大小，其实我的理解这个就是线程池的大小。举个简单的例子：\n\n　　假如有一个工厂，工厂里面有10个工人，每个工人同时只能做一件任务。\n\n　　因此只要当10个工人中有工人是空闲的，来了任务就分配给空闲的工人做；\n\n　　当10个工人都有任务在做时，如果还来了任务，就把任务进行排队等待；\n\n　　如果说新任务数目增长的速度远远大于工人做任务的速度，那么此时工厂主管可能会想补救措施，比如重新招4个临时工人进来；\n\n　　然后就将任务也分配给这4个临时工人做；\n\n　　如果说着14个工人做任务的速度还是不够，此时工厂主管可能就要考虑不再接收新的任务或者抛弃前面的一些任务了。\n\n　　当这14个工人当中有人空闲时，而新任务增长的速度又比较缓慢，工厂主管可能就考虑辞掉4个临时工了，只保持原来的10个工人，毕竟请额外的工人是要花钱的。\n\n \n\n　　这个例子中的corePoolSize就是10，而maximumPoolSize就是14（10+4）。\n\n　　也就是说corePoolSize就是线程池大小，maximumPoolSize在我看来是线程池的一种补救措施，即任务量突然过大时的一种补救措施。\n\n　　不过为了方便理解，在本文后面还是将corePoolSize翻译成核心池大小。\n\n　　largestPoolSize只是一个用来起记录作用的变量，用来记录线程池中曾经有过的最大线程数目，跟线程池的容量没有任何关系。\n\n \n\n　　下面我们进入正题，看一下任务从提交到最终执行完毕经历了哪些过程。\n\n　　在ThreadPoolExecutor类中，最核心的任务提交方法是execute()方法，虽然通过submit也可以提交任务，但是实际上submit方法里面最终调用的还是execute()方法，所以我们只需要研究execute()方法的实现原理即可：\n　　\n```java\npublic void execute(Runnable command) {\n    if (command == null)\n        throw new NullPointerException();\n    if (poolSize >= corePoolSize || !addIfUnderCorePoolSize(command)) {\n        if (runState == RUNNING && workQueue.offer(command)) {\n            if (runState != RUNNING || poolSize == 0)\n                ensureQueuedTaskHandled(command);\n        }\n        else if (!addIfUnderMaximumPoolSize(command))\n            reject(command); // is shutdown or saturated\n    }\n}\n```\n\n上面的代码可能看起来不是那么容易理解，下面我们一句一句解释：\n\n　　首先，判断提交的任务command是否为null，若是null，则抛出空指针异常；\n\n　　接着是这句，这句要好好理解一下：\n\n\n```java\nif (poolSize >= corePoolSize || !addIfUnderCorePoolSize(command))\n\n```\n\n由于是或条件运算符，所以先计算前半部分的值，如果线程池中当前线程数不小于核心池大小，那么就会直接进入下面的if语句块了。\n\n　　如果线程池中当前线程数小于核心池大小，则接着执行后半部分，也就是执行\n　　\n```java\naddIfUnderCorePoolSize(command)\n\n```\n\n　如果执行完addIfUnderCorePoolSize这个方法返回false，则继续执行下面的if语句块，否则整个方法就直接执行完毕了。\n\n　　如果执行完addIfUnderCorePoolSize这个方法返回false，然后接着判断：\n\n```java\nif (runState == RUNNING && workQueue.offer(command))\n\n```\n\n 　　如果当前线程池处于RUNNING状态，则将任务放入任务缓存队列；如果当前线程池不处于RUNNING状态或者任务放入缓存队列失败，则执行：\n\n\n\n```java\naddIfUnderMaximumPoolSize(command)\n\n```\n\n　如果执行addIfUnderMaximumPoolSize方法失败，则执行reject()方法进行任务拒绝处理。\n\n　　回到前面：\n　　\n　　\n\n```java\nif (runState == RUNNING && workQueue.offer(command))\n\n\n```\n\n 　　这句的执行，如果说当前线程池处于RUNNING状态且将任务放入任务缓存队列成功，则继续进行判断：\n\n```java\nif (runState != RUNNING || poolSize == 0)\n\n```\n\n 　　这句判断是为了防止在将此任务添加进任务缓存队列的同时其他线程突然调用shutdown或者shutdownNow方法关闭了线程池的一种应急措施。如果是这样就执行：\n\n\n```java\nensureQueuedTaskHandled(command)\n\n```\n\n进行应急处理，从名字可以看出是保证 添加到任务缓存队列中的任务得到处理。\n\n　　我们接着看2个关键方法的实现：addIfUnderCorePoolSize和addIfUnderMaximumPoolSize：\n　　\n```java\nprivate boolean addIfUnderCorePoolSize(Runnable firstTask) {\n    Thread t = null;\n    final ReentrantLock mainLock = this.mainLock;\n    mainLock.lock();\n    try {\n        if (poolSize < corePoolSize && runState == RUNNING)\n            t = addThread(firstTask);        //创建线程去执行firstTask任务   \n        } finally {\n        mainLock.unlock();\n    }\n    if (t == null)\n        return false;\n    t.start();\n    return true;\n}\n```\n\n这个是addIfUnderCorePoolSize方法的具体实现，从名字可以看出它的意图就是当低于核心吃大小时执行的方法。下面看其具体实现，首先获取到锁，因为这地方涉及到线程池状态的变化，先通过if语句判断当前线程池中的线程数目是否小于核心池大小，有朋友也许会有疑问：前面在execute()方法中不是已经判断过了吗，只有线程池当前线程数目小于核心池大小才会执行addIfUnderCorePoolSize方法的，为何这地方还要继续判断？原因很简单，前面的判断过程中并没有加锁，因此可能在execute方法判断的时候poolSize小于corePoolSize，而判断完之后，在其他线程中又向线程池提交了任务，就可能导致poolSize不小于corePoolSize了，所以需要在这个地方继续判断。然后接着判断线程池的状态是否为RUNNING，原因也很简单，因为有可能在其他线程中调用了shutdown或者shutdownNow方法。然后就是执行\n\n```java\nt = addThread(firstTask);\n```\n\n这个方法也非常关键，传进去的参数为提交的任务，返回值为Thread类型。然后接着在下面判断t是否为空，为空则表明创建线程失败（即poolSize>=corePoolSize或者runState不等于RUNNING），否则调用t.start()方法启动线程。\n\n　　我们来看一下addThread方法的实现：\n　　\n```java\nprivate Thread addThread(Runnable firstTask) {\n    Worker w = new Worker(firstTask);\n    Thread t = threadFactory.newThread(w);  //创建一个线程，执行任务   \n    if (t != null) {\n        w.thread = t;            //将创建的线程的引用赋值为w的成员变量       \n        workers.add(w);\n        int nt = ++poolSize;     //当前线程数加1       \n        if (nt > largestPoolSize)\n            largestPoolSize = nt;\n    }\n    return t;\n}\n```\n\n　　在addThread方法中，首先用提交的任务创建了一个Worker对象，然后调用线程工厂threadFactory创建了一个新的线程t，然后将线程t的引用赋值给了Worker对象的成员变量thread，接着通过workers.add(w)将Worker对象添加到工作集当中。\n\n　　下面我们看一下Worker类的实现：\n　　\n```java\nprivate final class Worker implements Runnable {\n    private final ReentrantLock runLock = new ReentrantLock();\n    private Runnable firstTask;\n    volatile long completedTasks;\n    Thread thread;\n    Worker(Runnable firstTask) {\n        this.firstTask = firstTask;\n    }\n    boolean isActive() {\n        return runLock.isLocked();\n    }\n    void interruptIfIdle() {\n        final ReentrantLock runLock = this.runLock;\n        if (runLock.tryLock()) {\n            try {\n        if (thread != Thread.currentThread())\n        thread.interrupt();\n            } finally {\n                runLock.unlock();\n            }\n        }\n    }\n    void interruptNow() {\n        thread.interrupt();\n    }\n \n    private void runTask(Runnable task) {\n        final ReentrantLock runLock = this.runLock;\n        runLock.lock();\n        try {\n            if (runState < STOP &&\n                Thread.interrupted() &&\n                runState >= STOP)\n            boolean ran = false;\n            beforeExecute(thread, task);   //beforeExecute方法是ThreadPoolExecutor类的一个方法，没有具体实现，用户可以根据\n            //自己需要重载这个方法和后面的afterExecute方法来进行一些统计信息，比如某个任务的执行时间等           \n            try {\n                task.run();\n                ran = true;\n                afterExecute(task, null);\n                ++completedTasks;\n            } catch (RuntimeException ex) {\n                if (!ran)\n                    afterExecute(task, ex);\n                throw ex;\n            }\n        } finally {\n            runLock.unlock();\n        }\n    }\n \n    public void run() {\n        try {\n            Runnable task = firstTask;\n            firstTask = null;\n            while (task != null || (task = getTask()) != null) {\n                runTask(task);\n                task = null;\n            }\n        } finally {\n            workerDone(this);   //当任务队列中没有任务时，进行清理工作       \n        }\n    }\n}\n```\n\n它实际上实现了Runnable接口，因此上面的Thread t = threadFactory.newThread(w);效果跟下面这句的效果基本一样：\n\n```java\nThread t = new Thread(w);\n```\n\n　相当于传进去了一个Runnable任务，在线程t中执行这个Runnable。\n\n　　既然Worker实现了Runnable接口，那么自然最核心的方法便是run()方法了：\n\n```java\npublic void run() {\n    try {\n        Runnable task = firstTask;\n        firstTask = null;\n        while (task != null || (task = getTask()) != null) {\n            runTask(task);\n            task = null;\n        }\n    } finally {\n        workerDone(this);\n    }\n}\n\n```\n\n　从run方法的实现可以看出，它首先执行的是通过构造器传进来的任务firstTask，在调用runTask()执行完firstTask之后，在while循环里面不断通过getTask()去取新的任务来执行，那么去哪里取呢？自然是从任务缓存队列里面去取，getTask是ThreadPoolExecutor类中的方法，并不是Worker类中的方法，下面是getTask方法的实现：\n　\n```java\nRunnable getTask() {\n    for (;;) {\n        try {\n            int state = runState;\n            if (state > SHUTDOWN)\n                return null;\n            Runnable r;\n            if (state == SHUTDOWN)  // Help drain queue\n                r = workQueue.poll();\n            else if (poolSize > corePoolSize || allowCoreThreadTimeOut) //如果线程数大于核心池大小或者允许为核心池线程设置空闲时间，\n                //则通过poll取任务，若等待一定的时间取不到任务，则返回null\n                r = workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS);\n            else\n                r = workQueue.take();\n            if (r != null)\n                return r;\n            if (workerCanExit()) {    //如果没取到任务，即r为null，则判断当前的worker是否可以退出\n                if (runState >= SHUTDOWN) // Wake up others\n                    interruptIdleWorkers();   //中断处于空闲状态的worker\n                return null;\n            }\n            // Else retry\n        } catch (InterruptedException ie) {\n            // On interruption, re-check runState\n        }\n    }\n}\n```\n\n　在getTask中，先判断当前线程池状态，如果runState大于SHUTDOWN（即为STOP或者TERMINATED），则直接返回null。\n\n　　如果runState为SHUTDOWN或者RUNNING，则从任务缓存队列取任务。\n\n　　如果当前线程池的线程数大于核心池大小corePoolSize或者允许为核心池中的线程设置空闲存活时间，则调用poll(time,timeUnit)来取任务，这个方法会等待一定的时间，如果取不到任务就返回null。\n\n　　然后判断取到的任务r是否为null，为null则通过调用workerCanExit()方法来判断当前worker是否可以退出，我们看一下workerCanExit()的实现：\n　　\n```java\nprivate boolean workerCanExit() {\n    final ReentrantLock mainLock = this.mainLock;\n    mainLock.lock();\n    boolean canExit;\n    //如果runState大于等于STOP，或者任务缓存队列为空了\n    //或者  允许为核心池线程设置空闲存活时间并且线程池中的线程数目大于1\n    try {\n        canExit = runState >= STOP ||\n            workQueue.isEmpty() ||\n            (allowCoreThreadTimeOut &&\n             poolSize > Math.max(1, corePoolSize));\n    } finally {\n        mainLock.unlock();\n    }\n    return canExit;\n}\n\n```\n\n　也就是说如果线程池处于STOP状态、或者任务队列已为空或者允许为核心池线程设置空闲存活时间并且线程数大于1时，允许worker退出。如果允许worker退出，则调用interruptIdleWorkers()中断处于空闲状态的worker，我们看一下interruptIdleWorkers()的实现：\n　\n```java\nvoid interruptIdleWorkers() {\n    final ReentrantLock mainLock = this.mainLock;\n    mainLock.lock();\n    try {\n        for (Worker w : workers)  //实际上调用的是worker的interruptIfIdle()方法\n            w.interruptIfIdle();\n    } finally {\n        mainLock.unlock();\n    }\n}\n```\n\n 　　从实现可以看出，它实际上调用的是worker的interruptIfIdle()方法，在worker的interruptIfIdle()方法中：\n\n```java\nvoid interruptIfIdle() {\n    final ReentrantLock runLock = this.runLock;\n    if (runLock.tryLock()) {    //注意这里，是调用tryLock()来获取锁的，因为如果当前worker正在执行任务，锁已经被获取了，是无法获取到锁的\n                                //如果成功获取了锁，说明当前worker处于空闲状态\n        try {\n    if (thread != Thread.currentThread())  \n    thread.interrupt();\n        } finally {\n            runLock.unlock();\n        }\n    }\n}\n\n```\n\n  　　这里有一个非常巧妙的设计方式，假如我们来设计线程池，可能会有一个任务分派线程，当发现有线程空闲时，就从任务缓存队列中取一个任务交给空闲线程执行。但是在这里，并没有采用这样的方式，因为这样会要额外地对任务分派线程进行管理，无形地会增加难度和复杂度，这里直接让执行完任务的线程去任务缓存队列里面取任务来执行。\n\n 　　我们再看addIfUnderMaximumPoolSize方法的实现，这个方法的实现思想和addIfUnderCorePoolSize方法的实现思想非常相似，唯一的区别在于addIfUnderMaximumPoolSize方法是在线程池中的线程数达到了核心池大小并且往任务队列中添加任务失败的情况下执行的：\n 　　\n```java\nprivate boolean addIfUnderMaximumPoolSize(Runnable firstTask) {\n    Thread t = null;\n    final ReentrantLock mainLock = this.mainLock;\n    mainLock.lock();\n    try {\n        if (poolSize < maximumPoolSize && runState == RUNNING)\n            t = addThread(firstTask);\n    } finally {\n        mainLock.unlock();\n    }\n    if (t == null)\n        return false;\n    t.start();\n    return true;\n}\n```\n\n　看到没有，其实它和addIfUnderCorePoolSize方法的实现基本一模一样，只是if语句判断条件中的poolSize < maximumPoolSize不同而已。\n\n　　到这里，大部分朋友应该对任务提交给线程池之后到被执行的整个过程有了一个基本的了解，下面总结一下：\n\n　　1）首先，要清楚corePoolSize和maximumPoolSize的含义；\n\n　　2）其次，要知道Worker是用来起到什么作用的；\n\n　　3）要知道任务提交给线程池之后的处理策略，这里总结一下主要有4点：\n\n如果当前线程池中的线程数目小于corePoolSize，则每来一个任务，就会创建一个线程去执行这个任务；  \n\n如果当前线程池中的线程数目>=corePoolSize，则每来一个任务，会尝试将其添加到任务缓存队列当中，若添加成功，则该任务会等待空闲线程将其取出去执行；若添加失败（一般来说是任务缓存队列已满），则会尝试创建新的线程去执行这个任务；  \n\n如果当前线程池中的线程数目达到maximumPoolSize，则会采取任务拒绝策略进行处理；  \n\n如果线程池中的线程数量大于 corePoolSize时，如果某线程空闲时间超过keepAliveTime，线程将被终止，直至线程池中的线程数目不大于corePoolSize；如果允许为核心池中的线程设置存活时间，那么核心池中的线程空闲时间超过keepAliveTime，线程也会被终止。  \n\n3.线程池中的线程初始化\n\n　　默认情况下，创建线程池之后，线程池中是没有线程的，需要提交任务之后才会创建线程。\n\n　　在实际中如果需要线程池创建之后立即创建线程，可以通过以下两个方法办到：\n\nprestartCoreThread()：初始化一个核心线程；\nprestartAllCoreThreads()：初始化所有核心线程\n　　下面是这2个方法的实现：\n```java\npublic boolean prestartCoreThread() {\n    return addIfUnderCorePoolSize(null); //注意传进去的参数是null\n}\n \npublic int prestartAllCoreThreads() {\n    int n = 0;\n    while (addIfUnderCorePoolSize(null))//注意传进去的参数是null\n        ++n;\n    return n;\n}\n\n```\n\n注意上面传进去的参数是null，根据第2小节的分析可知如果传进去的参数为null，则最后执行线程会阻塞在getTask方法中的\n\n```java\nr = workQueue.take();\n\n```\n\n 　　即等待任务队列中有任务。\n\n4.任务缓存队列及排队策略\n\n在前面我们多次提到了任务缓存队列，即workQueue，它用来存放等待执行的任务。\n\n　　workQueue的类型为BlockingQueue<Runnable>，通常可以取下面三种类型：\n\n　　1）ArrayBlockingQueue：基于数组的先进先出队列，此队列创建时必须指定大小；\n\n　　2）LinkedBlockingQueue：基于链表的先进先出队列，如果创建时没有指定此队列大小，则默认为Integer.MAX_VALUE；\n\n　　3）synchronousQueue：这个队列比较特殊，它不会保存提交的任务，而是将直接新建一个线程来执行新来的任务。\n\n5.任务拒绝策略\n\n　　当线程池的任务缓存队列已满并且线程池中的线程数目达到maximumPoolSize，如果还有任务到来就会采取任务拒绝策略，通常有以下四种策略：\n　　\n```java\nThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。\nThreadPoolExecutor.DiscardPolicy：也是丢弃任务，但是不抛出异常。\nThreadPoolExecutor.DiscardOldestPolicy：丢弃队列最前面的任务，然后重新尝试执行任务（重复此过程）\nThreadPoolExecutor.CallerRunsPolicy：由调用线程处理该任务\n```\n\n6.线程池的关闭\n　ThreadPoolExecutor提供了两个方法，用于线程池的关闭，分别是shutdown()和shutdownNow()，其中：\n\n>shutdown()：不会立即终止线程池，而是要等所有任务缓存队列中的任务都执行完后才终止，但再也不会接受新的任务  \n\n>shutdownNow()：立即终止线程池，并尝试打断正在执行的任务，并且清空任务缓存队列，返回尚未执行的任务\n\n7.线程池容量的动态调整\n　　ThreadPoolExecutor提供了动态调整线程池容量大小的方法：setCorePoolSize()和setMaximumPoolSize()，\n\n>setCorePoolSize：设置核心池大小  \n\n>setMaximumPoolSize：设置线程池最大能创建的线程数目大小  \n\n　　当上述参数从小变大时，ThreadPoolExecutor进行线程赋值，还可能立即创建新的线程来执行任务。\n　　\n　　\n- ## 三.使用示例\n\n　前面我们讨论了关于线程池的实现原理，这一节我们来看一下它的具体使用：\n　\n```java\n　public class Test {\n     public static void main(String[] args) {   \n         ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 200, TimeUnit.MILLISECONDS,\n                 new ArrayBlockingQueue<Runnable>(5));\n          \n         for(int i=0;i<15;i++){\n             MyTask myTask = new MyTask(i);\n             executor.execute(myTask);\n             System.out.println(\"线程池中线程数目：\"+executor.getPoolSize()+\"，队列中等待执行的任务数目：\"+\n             executor.getQueue().size()+\"，已执行玩别的任务数目：\"+executor.getCompletedTaskCount());\n         }\n         executor.shutdown();\n     }\n}\n \n \nclass MyTask implements Runnable {\n    private int taskNum;\n     \n    public MyTask(int num) {\n        this.taskNum = num;\n    }\n     \n    @Override\n    public void run() {\n        System.out.println(\"正在执行task \"+taskNum);\n        try {\n            Thread.currentThread().sleep(4000);\n        } catch (InterruptedException e) {\n            e.printStackTrace();\n        }\n        System.out.println(\"task \"+taskNum+\"执行完毕\");\n    }\n}\n```\n　\n执行结果：\n\n```java\n正在执行task 0\n线程池中线程数目：1，队列中等待执行的任务数目：0，已执行玩别的任务数目：0\n线程池中线程数目：2，队列中等待执行的任务数目：0，已执行玩别的任务数目：0\n正在执行task 1\n线程池中线程数目：3，队列中等待执行的任务数目：0，已执行玩别的任务数目：0\n正在执行task 2\n线程池中线程数目：4，队列中等待执行的任务数目：0，已执行玩别的任务数目：0\n正在执行task 3\n线程池中线程数目：5，队列中等待执行的任务数目：0，已执行玩别的任务数目：0\n正在执行task 4\n线程池中线程数目：5，队列中等待执行的任务数目：1，已执行玩别的任务数目：0\n线程池中线程数目：5，队列中等待执行的任务数目：2，已执行玩别的任务数目：0\n线程池中线程数目：5，队列中等待执行的任务数目：3，已执行玩别的任务数目：0\n线程池中线程数目：5，队列中等待执行的任务数目：4，已执行玩别的任务数目：0\n线程池中线程数目：5，队列中等待执行的任务数目：5，已执行玩别的任务数目：0\n线程池中线程数目：6，队列中等待执行的任务数目：5，已执行玩别的任务数目：0\n正在执行task 10\n线程池中线程数目：7，队列中等待执行的任务数目：5，已执行玩别的任务数目：0\n正在执行task 11\n线程池中线程数目：8，队列中等待执行的任务数目：5，已执行玩别的任务数目：0\n正在执行task 12\n线程池中线程数目：9，队列中等待执行的任务数目：5，已执行玩别的任务数目：0\n正在执行task 13\n线程池中线程数目：10，队列中等待执行的任务数目：5，已执行玩别的任务数目：0\n正在执行task 14\ntask 3执行完毕\ntask 0执行完毕\ntask 2执行完毕\ntask 1执行完毕\n正在执行task 8\n正在执行task 7\n正在执行task 6\n正在执行task 5\ntask 4执行完毕\ntask 10执行完毕\ntask 11执行完毕\ntask 13执行完毕\ntask 12执行完毕\n正在执行task 9\ntask 14执行完毕\ntask 8执行完毕\ntask 5执行完毕\ntask 7执行完毕\ntask 6执行完毕\ntask 9执行完毕\n```\n\n从执行结果可以看出，当线程池中线程的数目大于5时，便将任务放入任务缓存队列里面，当任务缓存队列满了之后，便创建新的线程。如果上面程序中，将for循环中改成执行20个任务，就会抛出任务拒绝异常了。\n\n　　不过在java doc中，并不提倡我们直接使用ThreadPoolExecutor，而是使用Executors类中提供的几个静态方法来创建线程池：\n　\n```java\nxecutors.newCachedThreadPool();        //创建一个缓冲池，缓冲池容量大小为Integer.MAX_VALUE\nExecutors.newSingleThreadExecutor();   //创建容量为1的缓冲池\nExecutors.newFixedThreadPool(int);    //创建固定容量大小的缓冲池\n```\n\n下面是这三个静态方法的具体实现;\n\n```java\npublic static ExecutorService newFixedThreadPool(int nThreads) {\n    return new ThreadPoolExecutor(nThreads, nThreads,\n                                  0L, TimeUnit.MILLISECONDS,\n                                  new LinkedBlockingQueue<Runnable>());\n}\npublic static ExecutorService newSingleThreadExecutor() {\n    return new FinalizableDelegatedExecutorService\n        (new ThreadPoolExecutor(1, 1,\n                                0L, TimeUnit.MILLISECONDS,\n                                new LinkedBlockingQueue<Runnable>()));\n}\npublic static ExecutorService newCachedThreadPool() {\n    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,\n                                  60L, TimeUnit.SECONDS,\n                                  new SynchronousQueue<Runnable>());\n}\n\n```\n\n从它们的具体实现来看，它们实际上也是调用了ThreadPoolExecutor，只不过参数都已配置好了。\n\n　　newFixedThreadPool创建的线程池corePoolSize和maximumPoolSize值是相等的，它使用的LinkedBlockingQueue；\n\n　　newSingleThreadExecutor将corePoolSize和maximumPoolSize都设置为1，也使用的LinkedBlockingQueue；\n\n　　newCachedThreadPool将corePoolSize设置为0，将maximumPoolSize设置为Integer.MAX_VALUE，使用的SynchronousQueue，也就是说来了任务就创建线程运行，当线程空闲超过60秒，就销毁线程。\n\n　　实际中，如果Executors提供的三个静态方法能满足要求，就尽量使用它提供的三个方法，因为自己去手动配置ThreadPoolExecutor的参数有点麻烦，要根据实际任务的类型和数量来进行配置。\n\n　　另外，如果ThreadPoolExecutor达不到要求，可以自己继承ThreadPoolExecutor类进行重写。\n　　\n- ## 四.如何合理配置线程池的大小 \n\n本节来讨论一个比较重要的话题：如何合理配置线程池大小，仅供参考。\n\n　　一般需要根据任务的类型来配置线程池大小：\n\n　　如果是CPU密集型任务，就需要尽量压榨CPU，参考值可以设为 NCPU+1\n\n　　如果是IO密集型任务，参考值可以设置为2*NCPU\n\n　　当然，这只是一个参考值，具体的设置还需要根据实际情况进行调整，比如可以先将线程池大小设置为参考值，再观察任务运行情况和系统负载、资源利用率来进行适当调整。\n\n　　参考资料：\n\n　　http://ifeve.com/java-threadpool/\n\n　　http://blog.163.com/among_1985/blog/static/275005232012618849266/\n\n　　http://developer.51cto.com/art/201203/321885.htm\n\n　　http://blog.csdn.net/java2000_wl/article/details/22097059\n\n　　http://blog.csdn.net/cutesource/article/details/6061229\n\n　　http://blog.csdn.net/xieyuooo/article/details/8718741', b'0', '2', '1', b'0', b'0', 'Java并发编程：线程池的使用', 'Java并发编程：线程池的使用', b'1', '2019-02-15 06:47:56', '2019-02-15 06:47:56'), ('9', '2019-2-14 随笔	', '1', 'xblog/cover/20190215144916522_20190213133014515_b79a2572eccb402a863cadce51ee20bc.jpg', null, b'1', '<p>我也记不清昨天发生了啥，离家悲伤的情绪已经没了，生活不就是嘻嘻哈哈，玩玩闹闹，噢噢噢噢，昨天得知一个噩耗，这周六上班，MMP。希望明天晴天。</p>\n', '我也记不清昨天发生了啥，离家悲伤的情绪已经没了，生活不就是嘻嘻哈哈，玩玩闹闹，噢噢噢噢，昨天得知一个噩耗，这周六上班，MMP。希望明天晴天。', b'0', '3', '1', b'0', b'1', '2019-12-14 随笔', '2019-12-14 随笔', b'1', '2019-02-15 06:49:34', '2019-02-15 06:49:34');
COMMIT;

-- ----------------------------
--  Table structure for `biz_article_look`
-- ----------------------------
DROP TABLE IF EXISTS `biz_article_look`;
CREATE TABLE `biz_article_look` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `article_id` int(11) unsigned NOT NULL COMMENT '文章ID',
  `user_id` int(11) unsigned DEFAULT NULL COMMENT '已登录用户ID',
  `user_ip` varchar(50) DEFAULT NULL COMMENT '用户IP',
  `look_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '浏览时间',
  `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '添加时间',
  `update_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=906 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT COMMENT='文章浏览';

-- ----------------------------
--  Records of `biz_article_look`
-- ----------------------------
BEGIN;
INSERT INTO `biz_article_look` VALUES ('1', '2', null, '218.74.30.199', '2019-01-30 03:01:14', '2019-01-30 11:01:14', '2019-01-30 11:01:14'), ('2', '2', null, '218.74.30.199', '2019-01-30 03:21:22', '2019-01-30 11:21:22', '2019-01-30 11:21:22'), ('3', '2', null, '218.74.30.199', '2019-01-30 03:24:07', '2019-01-30 11:24:07', '2019-01-30 11:24:07'), ('4', '2', null, '218.74.30.199', '2019-01-30 06:04:20', '2019-01-30 14:04:20', '2019-01-30 14:04:20'), ('5', '2', null, '218.74.30.199', '2019-01-30 06:08:14', '2019-01-30 14:08:14', '2019-01-30 14:08:14'), ('6', '2', null, '218.74.30.199', '2019-01-30 06:08:16', '2019-01-30 14:08:16', '2019-01-30 14:08:16'), ('7', '2', null, '218.74.30.199', '2019-01-30 06:08:19', '2019-01-30 14:08:18', '2019-01-30 14:08:18'), ('8', '2', null, '218.74.30.199', '2019-01-30 06:08:23', '2019-01-30 14:08:22', '2019-01-30 14:08:22'), ('9', '2', null, '180.163.220.5', '2019-01-30 06:08:28', '2019-01-30 14:08:27', '2019-01-30 14:08:27'), ('10', '2', null, '218.74.30.199', '2019-01-30 06:08:59', '2019-01-30 14:08:58', '2019-01-30 14:08:58'), ('11', '2', null, '61.130.108.38', '2019-01-30 06:11:38', '2019-01-30 14:11:37', '2019-01-30 14:11:37'), ('12', '2', null, '218.74.30.199', '2019-01-30 06:12:18', '2019-01-30 14:12:18', '2019-01-30 14:12:18'), ('13', '2', null, '218.74.30.199', '2019-01-30 06:13:18', '2019-01-30 14:13:18', '2019-01-30 14:13:18'), ('14', '2', null, '218.74.30.199', '2019-01-30 06:14:23', '2019-01-30 14:14:22', '2019-01-30 14:14:22'), ('15', '2', null, '218.74.30.199', '2019-01-30 06:17:47', '2019-01-30 14:17:47', '2019-01-30 14:17:47'), ('16', '2', null, '104.192.74.44', '2019-01-30 13:33:57', '2019-01-30 21:33:57', '2019-01-30 21:33:57'), ('17', '2', null, '104.192.74.44', '2019-01-30 13:34:03', '2019-01-30 21:34:02', '2019-01-30 21:34:02'), ('18', '2', null, '104.192.74.21', '2019-01-31 14:31:19', '2019-01-31 22:31:18', '2019-01-31 22:31:18'), ('19', '2', null, '104.192.74.21', '2019-01-31 14:31:22', '2019-01-31 22:31:22', '2019-01-31 22:31:22'), ('20', '2', null, '104.192.74.26', '2019-02-01 16:33:56', '2019-02-02 00:33:57', '2019-02-02 00:33:57'), ('21', '2', null, '104.192.74.26', '2019-02-01 16:34:02', '2019-02-02 00:34:02', '2019-02-02 00:34:02'), ('22', '2', null, '104.192.74.31', '2019-02-02 08:01:43', '2019-02-02 16:01:43', '2019-02-02 16:01:43'), ('23', '2', null, '104.192.74.31', '2019-02-02 08:01:46', '2019-02-02 16:01:45', '2019-02-02 16:01:45'), ('24', '3', null, '113.214.210.71', '2019-02-03 07:14:49', '2019-02-03 15:14:49', '2019-02-03 15:14:49'), ('25', '3', null, '104.192.74.36', '2019-02-03 07:52:31', '2019-02-03 15:52:30', '2019-02-03 15:52:30'), ('26', '3', null, '104.192.74.36', '2019-02-03 07:52:38', '2019-02-03 15:52:38', '2019-02-03 15:52:38'), ('27', '2', null, '104.192.74.36', '2019-02-03 07:52:40', '2019-02-03 15:52:39', '2019-02-03 15:52:39'), ('28', '2', null, '104.192.74.36', '2019-02-03 07:52:47', '2019-02-03 15:52:46', '2019-02-03 15:52:46'), ('29', '3', null, '104.192.74.25', '2019-02-04 09:45:57', '2019-02-04 17:45:57', '2019-02-04 17:45:57'), ('30', '2', null, '104.192.74.25', '2019-02-04 09:46:06', '2019-02-04 17:46:05', '2019-02-04 17:46:05'), ('31', '3', null, '104.192.74.25', '2019-02-04 09:46:07', '2019-02-04 17:46:06', '2019-02-04 17:46:06'), ('32', '2', null, '104.192.74.25', '2019-02-04 09:46:12', '2019-02-04 17:46:11', '2019-02-04 17:46:11'), ('33', '3', null, '112.17.245.17', '2019-02-05 03:03:22', '2019-02-05 11:03:22', '2019-02-05 11:03:22'), ('34', '3', null, '104.192.74.36', '2019-02-05 15:06:48', '2019-02-05 23:06:47', '2019-02-05 23:06:47'), ('35', '2', null, '104.192.74.36', '2019-02-05 15:06:50', '2019-02-05 23:06:49', '2019-02-05 23:06:49'), ('36', '3', null, '104.192.74.36', '2019-02-05 15:06:56', '2019-02-05 23:06:55', '2019-02-05 23:06:55'), ('37', '2', null, '104.192.74.36', '2019-02-05 15:06:58', '2019-02-05 23:06:58', '2019-02-05 23:06:58'), ('38', '3', null, '104.192.74.43', '2019-02-06 08:35:13', '2019-02-06 16:35:12', '2019-02-06 16:35:12'), ('39', '2', null, '104.192.74.43', '2019-02-06 08:35:21', '2019-02-06 16:35:21', '2019-02-06 16:35:21'), ('40', '3', null, '104.192.74.43', '2019-02-06 08:35:22', '2019-02-06 16:35:21', '2019-02-06 16:35:21'), ('41', '2', null, '104.192.74.43', '2019-02-06 08:35:27', '2019-02-06 16:35:27', '2019-02-06 16:35:27'), ('42', '3', null, '104.192.74.44', '2019-02-07 16:02:18', '2019-02-08 00:02:18', '2019-02-08 00:02:18'), ('43', '2', null, '104.192.74.44', '2019-02-07 16:02:27', '2019-02-08 00:02:26', '2019-02-08 00:02:26'), ('44', '3', null, '104.192.74.44', '2019-02-07 16:02:30', '2019-02-08 00:02:29', '2019-02-08 00:02:29'), ('45', '2', null, '104.192.74.44', '2019-02-07 16:02:31', '2019-02-08 00:02:30', '2019-02-08 00:02:30'), ('46', '3', null, '104.192.74.18', '2019-02-08 09:58:08', '2019-02-08 17:58:08', '2019-02-08 17:58:08'), ('47', '2', null, '104.192.74.18', '2019-02-08 09:58:14', '2019-02-08 17:58:14', '2019-02-08 17:58:14'), ('48', '3', null, '104.192.74.18', '2019-02-08 09:58:14', '2019-02-08 17:58:14', '2019-02-08 17:58:14'), ('49', '2', null, '104.192.74.18', '2019-02-08 09:58:22', '2019-02-08 17:58:21', '2019-02-08 17:58:21'), ('50', '3', null, '104.192.74.19', '2019-02-09 07:56:23', '2019-02-09 15:56:22', '2019-02-09 15:56:22'), ('51', '3', null, '104.192.74.19', '2019-02-09 07:56:30', '2019-02-09 15:56:29', '2019-02-09 15:56:29'), ('52', '2', null, '104.192.74.19', '2019-02-09 07:56:36', '2019-02-09 15:56:35', '2019-02-09 15:56:35'), ('53', '2', null, '104.192.74.19', '2019-02-09 07:56:41', '2019-02-09 15:56:40', '2019-02-09 15:56:40'), ('54', '3', null, '104.192.74.30', '2019-02-10 11:35:39', '2019-02-10 19:35:39', '2019-02-10 19:35:39'), ('55', '2', null, '104.192.74.30', '2019-02-10 11:35:42', '2019-02-10 19:35:42', '2019-02-10 19:35:42'), ('56', '3', null, '104.192.74.30', '2019-02-10 11:35:46', '2019-02-10 19:35:45', '2019-02-10 19:35:45'), ('57', '2', null, '104.192.74.30', '2019-02-10 11:35:48', '2019-02-10 19:35:47', '2019-02-10 19:35:47'), ('58', '3', null, '104.192.74.41', '2019-02-11 09:28:09', '2019-02-11 17:28:08', '2019-02-11 17:28:08'), ('59', '2', null, '104.192.74.41', '2019-02-11 09:28:12', '2019-02-11 17:28:11', '2019-02-11 17:28:11'), ('60', '3', null, '104.192.74.41', '2019-02-11 09:28:15', '2019-02-11 17:28:14', '2019-02-11 17:28:14'), ('61', '2', null, '104.192.74.41', '2019-02-11 09:28:23', '2019-02-11 17:28:22', '2019-02-11 17:28:22'), ('62', '3', null, '59.36.119.227', '2019-02-11 16:04:51', '2019-02-12 00:04:50', '2019-02-12 00:04:50'), ('63', '2', null, '58.251.121.184', '2019-02-11 16:24:17', '2019-02-12 00:24:16', '2019-02-12 00:24:16'), ('64', '3', null, '104.192.74.35', '2019-02-12 16:38:50', '2019-02-13 00:38:50', '2019-02-13 00:38:50'), ('65', '2', null, '104.192.74.35', '2019-02-12 16:38:51', '2019-02-13 00:38:50', '2019-02-13 00:38:50'), ('66', '3', null, '104.192.74.35', '2019-02-12 16:38:57', '2019-02-13 00:38:57', '2019-02-13 00:38:57'), ('67', '2', null, '104.192.74.35', '2019-02-12 16:39:02', '2019-02-13 00:39:02', '2019-02-13 00:39:02'), ('68', '2', null, '59.36.119.226', '2019-02-12 20:55:51', '2019-02-13 04:55:50', '2019-02-13 04:55:50'), ('69', '4', null, '122.233.194.154', '2019-02-13 05:30:48', '2019-02-13 13:30:47', '2019-02-13 13:30:47'), ('70', '4', null, '122.233.194.154', '2019-02-13 05:31:41', '2019-02-13 13:31:40', '2019-02-13 13:31:40'), ('71', '4', null, '122.233.194.154', '2019-02-13 05:33:15', '2019-02-13 13:33:14', '2019-02-13 13:33:14'), ('72', '4', null, '122.233.194.154', '2019-02-13 05:33:22', '2019-02-13 13:33:22', '2019-02-13 13:33:22'), ('73', '4', null, '122.233.194.154', '2019-02-13 05:33:28', '2019-02-13 13:33:27', '2019-02-13 13:33:27'), ('74', '4', null, '61.129.6.227', '2019-02-13 05:34:10', '2019-02-13 13:34:10', '2019-02-13 13:34:10'), ('75', '4', null, '61.130.108.38', '2019-02-13 05:34:14', '2019-02-13 13:34:14', '2019-02-13 13:34:14'), ('76', '4', null, '122.233.194.154', '2019-02-13 05:35:38', '2019-02-13 13:35:37', '2019-02-13 13:35:37'), ('77', '4', null, '61.130.108.38', '2019-02-13 05:36:58', '2019-02-13 13:36:58', '2019-02-13 13:36:58'), ('78', '4', null, '223.104.246.200', '2019-02-13 05:37:20', '2019-02-13 13:37:19', '2019-02-13 13:37:19'), ('79', '4', null, '122.233.194.154', '2019-02-13 05:40:29', '2019-02-13 13:40:29', '2019-02-13 13:40:29'), ('80', '4', null, '125.120.227.121', '2019-02-13 05:40:57', '2019-02-13 13:40:56', '2019-02-13 13:40:56'), ('81', '4', null, '42.236.10.78', '2019-02-13 05:41:00', '2019-02-13 13:40:59', '2019-02-13 13:40:59'), ('82', '2', null, '125.120.227.121', '2019-02-13 05:41:53', '2019-02-13 13:41:53', '2019-02-13 13:41:53'), ('83', '2', null, '180.163.220.66', '2019-02-13 05:42:01', '2019-02-13 13:42:00', '2019-02-13 13:42:00'), ('84', '4', null, '125.120.227.121', '2019-02-13 05:42:31', '2019-02-13 13:42:31', '2019-02-13 13:42:31'), ('85', '2', null, '125.120.227.121', '2019-02-13 05:43:18', '2019-02-13 13:43:18', '2019-02-13 13:43:18'), ('86', '4', null, '125.120.227.121', '2019-02-13 05:43:55', '2019-02-13 13:43:55', '2019-02-13 13:43:55'), ('87', '4', null, '122.233.194.154', '2019-02-13 05:44:56', '2019-02-13 13:44:56', '2019-02-13 13:44:56'), ('88', '4', null, '101.227.139.161', '2019-02-13 05:45:05', '2019-02-13 13:45:04', '2019-02-13 13:45:04'), ('89', '4', null, '122.233.194.154', '2019-02-13 05:45:12', '2019-02-13 13:45:11', '2019-02-13 13:45:11'), ('90', '4', null, '113.214.210.121', '2019-02-13 05:45:17', '2019-02-13 13:45:17', '2019-02-13 13:45:17'), ('91', '4', null, '122.233.194.154', '2019-02-13 05:45:47', '2019-02-13 13:45:47', '2019-02-13 13:45:47'), ('92', '4', null, '122.233.194.154', '2019-02-13 05:46:48', '2019-02-13 13:46:47', '2019-02-13 13:46:47'), ('93', '2', null, '122.233.194.154', '2019-02-13 05:48:10', '2019-02-13 13:48:10', '2019-02-13 13:48:10'), ('94', '4', null, '122.233.194.154', '2019-02-13 05:48:24', '2019-02-13 13:48:24', '2019-02-13 13:48:24'), ('95', '4', null, '122.233.194.154', '2019-02-13 05:49:31', '2019-02-13 13:49:31', '2019-02-13 13:49:31'), ('96', '3', null, '122.233.194.154', '2019-02-13 05:50:02', '2019-02-13 13:50:02', '2019-02-13 13:50:02'), ('97', '3', null, '101.89.29.94', '2019-02-13 05:51:02', '2019-02-13 13:51:02', '2019-02-13 13:51:02'), ('98', '4', null, '112.17.240.135', '2019-02-13 05:53:53', '2019-02-13 13:53:53', '2019-02-13 13:53:53'), ('99', '4', null, '223.104.246.27', '2019-02-13 05:54:13', '2019-02-13 13:54:13', '2019-02-13 13:54:13'), ('100', '4', null, '122.233.194.154', '2019-02-13 05:55:22', '2019-02-13 13:55:22', '2019-02-13 13:55:22'), ('101', '4', null, '223.104.210.161', '2019-02-13 05:58:25', '2019-02-13 13:58:25', '2019-02-13 13:58:25'), ('102', '4', null, '125.120.227.121', '2019-02-13 05:59:45', '2019-02-13 13:59:45', '2019-02-13 13:59:45'), ('103', '4', null, '122.233.194.154', '2019-02-13 06:03:15', '2019-02-13 14:03:15', '2019-02-13 14:03:15'), ('104', '4', null, '122.233.194.154', '2019-02-13 06:04:41', '2019-02-13 14:04:41', '2019-02-13 14:04:41'), ('105', '3', null, '122.233.194.154', '2019-02-13 06:05:00', '2019-02-13 14:04:59', '2019-02-13 14:04:59'), ('106', '4', null, '122.233.194.154', '2019-02-13 06:08:04', '2019-02-13 14:08:04', '2019-02-13 14:08:04'), ('107', '4', null, '122.233.194.154', '2019-02-13 06:11:42', '2019-02-13 14:11:41', '2019-02-13 14:11:41'), ('108', '4', null, '101.89.239.216', '2019-02-13 06:12:41', '2019-02-13 14:12:41', '2019-02-13 14:12:41'), ('109', '4', null, '115.225.118.68', '2019-02-13 06:33:34', '2019-02-13 14:33:33', '2019-02-13 14:33:33'), ('110', '4', null, '223.104.246.200', '2019-02-13 06:35:33', '2019-02-13 14:35:33', '2019-02-13 14:35:33'), ('111', '4', null, '122.233.194.154', '2019-02-13 08:20:28', '2019-02-13 16:20:28', '2019-02-13 16:20:28'), ('112', '4', '1', '122.233.194.154', '2019-02-13 08:22:38', '2019-02-13 16:22:37', '2019-02-13 16:22:37'), ('113', '4', '1', '122.233.194.154', '2019-02-13 08:22:59', '2019-02-13 16:22:58', '2019-02-13 16:22:58'), ('114', '4', '1', '122.233.194.154', '2019-02-13 08:23:03', '2019-02-13 16:23:02', '2019-02-13 16:23:02'), ('115', '4', '1', '122.233.194.154', '2019-02-13 08:23:24', '2019-02-13 16:23:24', '2019-02-13 16:23:24'), ('116', '4', '1', '122.233.194.154', '2019-02-13 08:24:47', '2019-02-13 16:24:47', '2019-02-13 16:24:47'), ('117', '4', '1', '122.233.194.154', '2019-02-13 08:27:15', '2019-02-13 16:27:15', '2019-02-13 16:27:15'), ('118', '3', null, '163.177.90.152', '2019-02-13 10:51:13', '2019-02-13 18:51:12', '2019-02-13 18:51:12'), ('119', '4', null, '101.71.197.122', '2019-02-13 14:26:41', '2019-02-13 22:26:41', '2019-02-13 22:26:41'), ('120', '4', null, '223.104.246.91', '2019-02-13 14:26:47', '2019-02-13 22:26:46', '2019-02-13 22:26:46'), ('121', '4', null, '104.192.74.43', '2019-02-13 17:33:15', '2019-02-14 01:33:15', '2019-02-14 01:33:15'), ('122', '4', null, '104.192.74.43', '2019-02-13 17:33:23', '2019-02-14 01:33:22', '2019-02-14 01:33:22'), ('123', '3', null, '104.192.74.43', '2019-02-13 17:33:27', '2019-02-14 01:33:26', '2019-02-14 01:33:26'), ('124', '2', null, '104.192.74.43', '2019-02-13 17:33:31', '2019-02-14 01:33:30', '2019-02-14 01:33:30'), ('125', '2', null, '104.192.74.43', '2019-02-13 17:33:37', '2019-02-14 01:33:36', '2019-02-14 01:33:36'), ('126', '3', null, '104.192.74.43', '2019-02-13 17:33:37', '2019-02-14 01:33:36', '2019-02-14 01:33:36'), ('127', '7', null, '122.233.194.154', '2019-02-14 03:37:54', '2019-02-14 11:37:53', '2019-02-14 11:37:53'), ('128', '7', null, '122.233.194.154', '2019-02-14 05:53:46', '2019-02-14 13:53:46', '2019-02-14 13:53:46'), ('129', '7', null, '122.233.194.154', '2019-02-14 06:10:19', '2019-02-14 14:10:18', '2019-02-14 14:10:18'), ('130', '7', null, '101.227.139.172', '2019-02-14 06:10:24', '2019-02-14 14:10:24', '2019-02-14 14:10:24'), ('131', '7', null, '220.187.224.207', '2019-02-14 06:10:33', '2019-02-14 14:10:32', '2019-02-14 14:10:32'), ('132', '7', null, '112.17.247.117', '2019-02-14 06:11:01', '2019-02-14 14:11:01', '2019-02-14 14:11:01'), ('133', '7', null, '101.89.29.97', '2019-02-14 06:11:23', '2019-02-14 14:11:23', '2019-02-14 14:11:23'), ('134', '7', null, '112.17.247.117', '2019-02-14 06:12:12', '2019-02-14 14:12:11', '2019-02-14 14:12:11'), ('135', '6', null, '112.17.247.117', '2019-02-14 06:12:20', '2019-02-14 14:12:20', '2019-02-14 14:12:20'), ('136', '3', null, '122.233.194.154', '2019-02-14 06:12:40', '2019-02-14 14:12:39', '2019-02-14 14:12:39'), ('137', '3', null, '101.227.139.161', '2019-02-14 06:12:50', '2019-02-14 14:12:49', '2019-02-14 14:12:49'), ('138', '3', null, '112.17.247.117', '2019-02-14 06:12:52', '2019-02-14 14:12:51', '2019-02-14 14:12:51'), ('139', '3', null, '220.187.224.207', '2019-02-14 06:12:56', '2019-02-14 14:12:55', '2019-02-14 14:12:55'), ('140', '6', null, '101.89.29.97', '2019-02-14 06:13:20', '2019-02-14 14:13:20', '2019-02-14 14:13:20'), ('141', '3', null, '61.151.207.205', '2019-02-14 06:13:51', '2019-02-14 14:13:51', '2019-02-14 14:13:51'), ('142', '7', null, '61.130.108.38', '2019-02-14 06:42:38', '2019-02-14 14:42:37', '2019-02-14 14:42:37'), ('143', '7', null, '122.233.194.154', '2019-02-14 07:43:19', '2019-02-14 15:43:18', '2019-02-14 15:43:18'), ('144', '2', null, '122.233.194.154', '2019-02-14 08:59:42', '2019-02-14 16:59:42', '2019-02-14 16:59:42'), ('145', '6', null, '104.192.74.28', '2019-02-14 14:58:00', '2019-02-14 22:57:59', '2019-02-14 22:57:59'), ('146', '7', null, '104.192.74.28', '2019-02-14 14:58:00', '2019-02-14 22:57:59', '2019-02-14 22:57:59'), ('147', '5', null, '104.192.74.28', '2019-02-14 14:58:07', '2019-02-14 22:58:07', '2019-02-14 22:58:07'), ('148', '7', null, '104.192.74.28', '2019-02-14 14:58:10', '2019-02-14 22:58:10', '2019-02-14 22:58:10'), ('149', '6', null, '104.192.74.28', '2019-02-14 14:58:13', '2019-02-14 22:58:13', '2019-02-14 22:58:13'), ('150', '4', null, '104.192.74.28', '2019-02-14 14:58:18', '2019-02-14 22:58:18', '2019-02-14 22:58:18'), ('151', '5', null, '104.192.74.28', '2019-02-14 14:58:22', '2019-02-14 22:58:21', '2019-02-14 22:58:21'), ('152', '3', null, '104.192.74.28', '2019-02-14 14:58:22', '2019-02-14 22:58:22', '2019-02-14 22:58:22'), ('153', '4', null, '104.192.74.28', '2019-02-14 14:58:25', '2019-02-14 22:58:25', '2019-02-14 22:58:25'), ('154', '2', null, '104.192.74.28', '2019-02-14 14:58:34', '2019-02-14 22:58:34', '2019-02-14 22:58:34'), ('155', '3', null, '104.192.74.28', '2019-02-14 14:58:45', '2019-02-14 22:58:44', '2019-02-14 22:58:44'), ('156', '2', null, '104.192.74.28', '2019-02-14 14:58:52', '2019-02-14 22:58:52', '2019-02-14 22:58:52'), ('157', '9', null, '104.192.74.36', '2019-02-15 14:32:09', '2019-02-15 22:32:09', '2019-02-15 22:32:09'), ('158', '8', null, '104.192.74.36', '2019-02-15 14:32:10', '2019-02-15 22:32:09', '2019-02-15 22:32:09'), ('159', '7', null, '104.192.74.36', '2019-02-15 14:32:11', '2019-02-15 22:32:10', '2019-02-15 22:32:10'), ('160', '9', null, '104.192.74.36', '2019-02-15 14:32:20', '2019-02-15 22:32:20', '2019-02-15 22:32:20'), ('161', '7', null, '104.192.74.36', '2019-02-15 14:32:25', '2019-02-15 22:32:24', '2019-02-15 22:32:24'), ('162', '6', null, '104.192.74.36', '2019-02-15 14:32:25', '2019-02-15 22:32:25', '2019-02-15 22:32:25'), ('163', '5', null, '104.192.74.36', '2019-02-15 14:32:30', '2019-02-15 22:32:30', '2019-02-15 22:32:30'), ('164', '8', null, '104.192.74.36', '2019-02-15 14:32:31', '2019-02-15 22:32:31', '2019-02-15 22:32:31'), ('165', '6', null, '104.192.74.36', '2019-02-15 14:32:47', '2019-02-15 22:32:47', '2019-02-15 22:32:47'), ('166', '5', null, '104.192.74.36', '2019-02-15 14:32:48', '2019-02-15 22:32:47', '2019-02-15 22:32:47'), ('167', '4', null, '104.192.74.36', '2019-02-15 14:32:49', '2019-02-15 22:32:48', '2019-02-15 22:32:48'), ('168', '4', null, '104.192.74.36', '2019-02-15 14:33:03', '2019-02-15 22:33:02', '2019-02-15 22:33:02'), ('169', '2', null, '104.192.74.36', '2019-02-15 14:33:03', '2019-02-15 22:33:02', '2019-02-15 22:33:02'), ('170', '3', null, '104.192.74.36', '2019-02-15 14:33:03', '2019-02-15 22:33:03', '2019-02-15 22:33:03'), ('171', '2', null, '104.192.74.36', '2019-02-15 14:33:17', '2019-02-15 22:33:17', '2019-02-15 22:33:17'), ('172', '3', null, '104.192.74.36', '2019-02-15 14:33:23', '2019-02-15 22:33:23', '2019-02-15 22:33:23'), ('173', '4', null, '223.104.246.6', '2019-02-16 02:04:17', '2019-02-16 10:04:17', '2019-02-16 10:04:17'), ('174', '9', null, '223.104.246.6', '2019-02-16 02:04:55', '2019-02-16 10:04:54', '2019-02-16 10:04:54'), ('175', '4', null, '61.129.6.203', '2019-02-16 02:05:15', '2019-02-16 10:05:15', '2019-02-16 10:05:15'), ('176', '7', null, '218.74.29.236', '2019-02-16 02:05:33', '2019-02-16 10:05:33', '2019-02-16 10:05:33'), ('177', '9', null, '218.74.29.236', '2019-02-16 02:05:53', '2019-02-16 10:05:53', '2019-02-16 10:05:53'), ('178', '9', null, '61.129.6.159', '2019-02-16 02:05:54', '2019-02-16 10:05:54', '2019-02-16 10:05:54'), ('179', '9', null, '223.104.246.6', '2019-02-16 02:06:07', '2019-02-16 10:06:07', '2019-02-16 10:06:07'), ('180', '7', null, '223.104.246.6', '2019-02-16 02:06:35', '2019-02-16 10:06:34', '2019-02-16 10:06:34'), ('181', '9', null, '218.74.29.236', '2019-02-16 02:06:56', '2019-02-16 10:06:56', '2019-02-16 10:06:56'), ('182', '9', null, '218.74.29.236', '2019-02-16 02:06:58', '2019-02-16 10:06:57', '2019-02-16 10:06:57'), ('183', '7', null, '61.129.6.251', '2019-02-16 02:07:35', '2019-02-16 10:07:35', '2019-02-16 10:07:35'), ('184', '9', null, '104.192.74.39', '2019-02-16 14:34:28', '2019-02-16 22:34:28', '2019-02-16 22:34:28'), ('185', '8', null, '104.192.74.39', '2019-02-16 14:34:37', '2019-02-16 22:34:36', '2019-02-16 22:34:36'), ('186', '9', null, '104.192.74.39', '2019-02-16 14:34:38', '2019-02-16 22:34:38', '2019-02-16 22:34:38'), ('187', '7', null, '104.192.74.39', '2019-02-16 14:34:50', '2019-02-16 22:34:50', '2019-02-16 22:34:50'), ('188', '6', null, '104.192.74.39', '2019-02-16 14:34:52', '2019-02-16 22:34:51', '2019-02-16 22:34:51'), ('189', '8', null, '104.192.74.39', '2019-02-16 14:34:57', '2019-02-16 22:34:56', '2019-02-16 22:34:56'), ('190', '7', null, '104.192.74.39', '2019-02-16 14:34:58', '2019-02-16 22:34:57', '2019-02-16 22:34:57'), ('191', '5', null, '104.192.74.39', '2019-02-16 14:35:10', '2019-02-16 22:35:09', '2019-02-16 22:35:09'), ('192', '6', null, '104.192.74.39', '2019-02-16 14:35:12', '2019-02-16 22:35:11', '2019-02-16 22:35:11'), ('193', '4', null, '104.192.74.39', '2019-02-16 14:35:22', '2019-02-16 22:35:22', '2019-02-16 22:35:22'), ('194', '5', null, '104.192.74.39', '2019-02-16 14:35:24', '2019-02-16 22:35:23', '2019-02-16 22:35:23'), ('195', '4', null, '104.192.74.39', '2019-02-16 14:35:28', '2019-02-16 22:35:27', '2019-02-16 22:35:27'), ('196', '3', null, '104.192.74.39', '2019-02-16 14:35:30', '2019-02-16 22:35:29', '2019-02-16 22:35:29'), ('197', '2', null, '104.192.74.39', '2019-02-16 14:35:36', '2019-02-16 22:35:36', '2019-02-16 22:35:36'), ('198', '3', null, '104.192.74.39', '2019-02-16 14:35:52', '2019-02-16 22:35:51', '2019-02-16 22:35:51'), ('199', '2', null, '104.192.74.39', '2019-02-16 14:35:52', '2019-02-16 22:35:52', '2019-02-16 22:35:52'), ('200', '4', null, '124.90.197.69', '2019-02-16 15:56:09', '2019-02-16 23:56:09', '2019-02-16 23:56:09'), ('201', '9', null, '104.192.74.15', '2019-02-17 07:27:13', '2019-02-17 15:27:13', '2019-02-17 15:27:13'), ('202', '8', null, '104.192.74.15', '2019-02-17 07:27:14', '2019-02-17 15:27:13', '2019-02-17 15:27:13'), ('203', '7', null, '104.192.74.15', '2019-02-17 07:27:21', '2019-02-17 15:27:21', '2019-02-17 15:27:21'), ('204', '9', null, '104.192.74.15', '2019-02-17 07:27:32', '2019-02-17 15:27:32', '2019-02-17 15:27:32'), ('205', '7', null, '104.192.74.15', '2019-02-17 07:27:35', '2019-02-17 15:27:35', '2019-02-17 15:27:35'), ('206', '8', null, '104.192.74.15', '2019-02-17 07:27:36', '2019-02-17 15:27:36', '2019-02-17 15:27:36'), ('207', '6', null, '104.192.74.15', '2019-02-17 07:27:48', '2019-02-17 15:27:47', '2019-02-17 15:27:47'), ('208', '5', null, '104.192.74.15', '2019-02-17 07:27:50', '2019-02-17 15:27:50', '2019-02-17 15:27:50'), ('209', '5', null, '104.192.74.15', '2019-02-17 07:28:13', '2019-02-17 15:28:12', '2019-02-17 15:28:12'), ('210', '4', null, '104.192.74.15', '2019-02-17 07:28:18', '2019-02-17 15:28:18', '2019-02-17 15:28:18'), ('211', '3', null, '104.192.74.15', '2019-02-17 07:28:20', '2019-02-17 15:28:20', '2019-02-17 15:28:20'), ('212', '4', null, '104.192.74.15', '2019-02-17 07:28:40', '2019-02-17 15:28:39', '2019-02-17 15:28:39'), ('213', '2', null, '104.192.74.15', '2019-02-17 07:28:41', '2019-02-17 15:28:40', '2019-02-17 15:28:40'), ('214', '3', null, '104.192.74.15', '2019-02-17 07:28:45', '2019-02-17 15:28:44', '2019-02-17 15:28:44'), ('215', '2', null, '104.192.74.15', '2019-02-17 07:29:02', '2019-02-17 15:29:01', '2019-02-17 15:29:01'), ('216', '9', null, '104.192.74.39', '2019-02-18 12:44:22', '2019-02-18 20:44:22', '2019-02-18 20:44:22'), ('217', '8', null, '104.192.74.39', '2019-02-18 12:44:24', '2019-02-18 20:44:23', '2019-02-18 20:44:23'), ('218', '7', null, '104.192.74.39', '2019-02-18 12:44:34', '2019-02-18 20:44:33', '2019-02-18 20:44:33'), ('219', '9', null, '104.192.74.39', '2019-02-18 12:44:37', '2019-02-18 20:44:37', '2019-02-18 20:44:37'), ('220', '7', null, '104.192.74.39', '2019-02-18 12:44:40', '2019-02-18 20:44:39', '2019-02-18 20:44:39'), ('221', '8', null, '104.192.74.39', '2019-02-18 12:44:44', '2019-02-18 20:44:44', '2019-02-18 20:44:44'), ('222', '6', null, '104.192.74.39', '2019-02-18 12:44:45', '2019-02-18 20:44:45', '2019-02-18 20:44:45'), ('223', '5', null, '104.192.74.39', '2019-02-18 12:44:49', '2019-02-18 20:44:48', '2019-02-18 20:44:48'), ('224', '5', null, '104.192.74.39', '2019-02-18 12:45:04', '2019-02-18 20:45:04', '2019-02-18 20:45:04'), ('225', '6', null, '104.192.74.39', '2019-02-18 12:45:07', '2019-02-18 20:45:06', '2019-02-18 20:45:06'), ('226', '4', null, '104.192.74.39', '2019-02-18 12:45:10', '2019-02-18 20:45:10', '2019-02-18 20:45:10'), ('227', '4', null, '104.192.74.39', '2019-02-18 12:45:19', '2019-02-18 20:45:19', '2019-02-18 20:45:19'), ('228', '3', null, '104.192.74.39', '2019-02-18 12:45:20', '2019-02-18 20:45:19', '2019-02-18 20:45:19'), ('229', '2', null, '104.192.74.39', '2019-02-18 12:45:20', '2019-02-18 20:45:20', '2019-02-18 20:45:20'), ('230', '2', null, '104.192.74.39', '2019-02-18 12:45:41', '2019-02-18 20:45:40', '2019-02-18 20:45:40'), ('231', '3', null, '104.192.74.39', '2019-02-18 12:45:42', '2019-02-18 20:45:42', '2019-02-18 20:45:42'), ('232', '8', null, '115.200.243.177', '2019-02-19 08:51:15', '2019-02-19 16:51:15', '2019-02-19 16:51:15'), ('233', '8', null, '115.200.243.177', '2019-02-19 08:51:16', '2019-02-19 16:51:16', '2019-02-19 16:51:16'), ('234', '8', null, '115.200.243.177', '2019-02-19 08:51:16', '2019-02-19 16:51:17', '2019-02-19 16:51:17'), ('235', '8', null, '115.200.243.177', '2019-02-19 08:51:18', '2019-02-19 16:51:17', '2019-02-19 16:51:17'), ('236', '8', null, '115.200.243.177', '2019-02-19 08:51:23', '2019-02-19 16:51:22', '2019-02-19 16:51:22'), ('237', '8', null, '115.200.243.177', '2019-02-19 08:51:23', '2019-02-19 16:51:23', '2019-02-19 16:51:23'), ('238', '8', null, '115.200.243.177', '2019-02-19 08:51:24', '2019-02-19 16:51:23', '2019-02-19 16:51:23'), ('239', '8', null, '115.200.243.177', '2019-02-19 08:51:24', '2019-02-19 16:51:24', '2019-02-19 16:51:24'), ('240', '6', null, '115.200.243.177', '2019-02-19 08:51:26', '2019-02-19 16:51:26', '2019-02-19 16:51:26'), ('241', '6', null, '115.200.243.177', '2019-02-19 08:51:28', '2019-02-19 16:51:28', '2019-02-19 16:51:28'), ('242', '6', null, '115.200.243.177', '2019-02-19 08:51:28', '2019-02-19 16:51:28', '2019-02-19 16:51:28'), ('243', '2', null, '115.200.243.177', '2019-02-19 08:51:31', '2019-02-19 16:51:31', '2019-02-19 16:51:31'), ('244', '2', null, '115.200.243.177', '2019-02-19 08:51:31', '2019-02-19 16:51:31', '2019-02-19 16:51:31'), ('245', '2', null, '115.200.243.177', '2019-02-19 08:51:32', '2019-02-19 16:51:31', '2019-02-19 16:51:31'), ('246', '2', null, '115.200.243.177', '2019-02-19 08:51:32', '2019-02-19 16:51:31', '2019-02-19 16:51:31'), ('247', '2', null, '115.200.243.177', '2019-02-19 08:51:32', '2019-02-19 16:51:31', '2019-02-19 16:51:31'), ('248', '2', null, '115.200.243.177', '2019-02-19 08:51:32', '2019-02-19 16:51:32', '2019-02-19 16:51:32'), ('249', '2', null, '115.200.243.177', '2019-02-19 08:51:33', '2019-02-19 16:51:32', '2019-02-19 16:51:32'), ('250', '2', null, '115.200.243.177', '2019-02-19 08:51:33', '2019-02-19 16:51:32', '2019-02-19 16:51:32'), ('251', '2', null, '115.200.243.177', '2019-02-19 08:51:33', '2019-02-19 16:51:32', '2019-02-19 16:51:32'), ('252', '2', null, '115.200.243.177', '2019-02-19 08:51:33', '2019-02-19 16:51:33', '2019-02-19 16:51:33'), ('253', '8', null, '61.151.178.164', '2019-02-19 08:52:16', '2019-02-19 16:52:18', '2019-02-19 16:52:18'), ('254', '6', null, '101.227.139.194', '2019-02-19 08:52:31', '2019-02-19 16:52:31', '2019-02-19 16:52:31'), ('255', '2', null, '61.151.178.175', '2019-02-19 08:52:32', '2019-02-19 16:52:34', '2019-02-19 16:52:34'), ('256', '8', null, '124.160.214.71', '2019-02-19 09:02:53', '2019-02-19 17:02:53', '2019-02-19 17:02:53'), ('257', '8', null, '42.236.10.78', '2019-02-19 09:03:03', '2019-02-19 17:03:03', '2019-02-19 17:03:03'), ('258', '9', null, '104.192.74.21', '2019-02-19 10:23:17', '2019-02-19 18:23:17', '2019-02-19 18:23:17'), ('259', '8', null, '104.192.74.21', '2019-02-19 10:23:20', '2019-02-19 18:23:19', '2019-02-19 18:23:19'), ('260', '9', null, '104.192.74.21', '2019-02-19 10:23:28', '2019-02-19 18:23:27', '2019-02-19 18:23:27'), ('261', '7', null, '104.192.74.21', '2019-02-19 10:23:35', '2019-02-19 18:23:34', '2019-02-19 18:23:34'), ('262', '6', null, '104.192.74.21', '2019-02-19 10:23:35', '2019-02-19 18:23:34', '2019-02-19 18:23:34'), ('263', '8', null, '104.192.74.21', '2019-02-19 10:23:39', '2019-02-19 18:23:38', '2019-02-19 18:23:38'), ('264', '7', null, '104.192.74.21', '2019-02-19 10:23:49', '2019-02-19 18:23:49', '2019-02-19 18:23:49'), ('265', '6', null, '104.192.74.21', '2019-02-19 10:23:53', '2019-02-19 18:23:53', '2019-02-19 18:23:53'), ('266', '5', null, '104.192.74.21', '2019-02-19 10:23:54', '2019-02-19 18:23:54', '2019-02-19 18:23:54'), ('267', '4', null, '104.192.74.21', '2019-02-19 10:23:55', '2019-02-19 18:23:55', '2019-02-19 18:23:55'), ('268', '3', null, '104.192.74.21', '2019-02-19 10:24:08', '2019-02-19 18:24:08', '2019-02-19 18:24:08'), ('269', '4', null, '104.192.74.21', '2019-02-19 10:24:09', '2019-02-19 18:24:08', '2019-02-19 18:24:08'), ('270', '5', null, '104.192.74.21', '2019-02-19 10:24:09', '2019-02-19 18:24:09', '2019-02-19 18:24:09'), ('271', '2', null, '104.192.74.21', '2019-02-19 10:24:24', '2019-02-19 18:24:24', '2019-02-19 18:24:24'), ('272', '3', null, '104.192.74.21', '2019-02-19 10:24:25', '2019-02-19 18:24:25', '2019-02-19 18:24:25'), ('273', '2', null, '104.192.74.21', '2019-02-19 10:24:43', '2019-02-19 18:24:42', '2019-02-19 18:24:42'), ('274', '9', null, '104.192.74.23', '2019-02-20 06:45:17', '2019-02-20 14:45:17', '2019-02-20 14:45:17'), ('275', '8', null, '104.192.74.23', '2019-02-20 06:45:23', '2019-02-20 14:45:23', '2019-02-20 14:45:23'), ('276', '9', null, '104.192.74.23', '2019-02-20 06:45:31', '2019-02-20 14:45:31', '2019-02-20 14:45:31'), ('277', '7', null, '104.192.74.23', '2019-02-20 06:45:41', '2019-02-20 14:45:41', '2019-02-20 14:45:41'), ('278', '8', null, '104.192.74.23', '2019-02-20 06:45:45', '2019-02-20 14:45:45', '2019-02-20 14:45:45'), ('279', '6', null, '104.192.74.23', '2019-02-20 06:45:48', '2019-02-20 14:45:48', '2019-02-20 14:45:48'), ('280', '7', null, '104.192.74.23', '2019-02-20 06:46:02', '2019-02-20 14:46:01', '2019-02-20 14:46:01'), ('281', '6', null, '104.192.74.23', '2019-02-20 06:46:02', '2019-02-20 14:46:02', '2019-02-20 14:46:02'), ('282', '5', null, '104.192.74.23', '2019-02-20 06:46:09', '2019-02-20 14:46:08', '2019-02-20 14:46:08'), ('283', '4', null, '104.192.74.23', '2019-02-20 06:46:15', '2019-02-20 14:46:15', '2019-02-20 14:46:15'), ('284', '3', null, '104.192.74.23', '2019-02-20 06:46:19', '2019-02-20 14:46:18', '2019-02-20 14:46:18'), ('285', '4', null, '104.192.74.23', '2019-02-20 06:46:25', '2019-02-20 14:46:24', '2019-02-20 14:46:24'), ('286', '5', null, '104.192.74.23', '2019-02-20 06:46:28', '2019-02-20 14:46:28', '2019-02-20 14:46:28'), ('287', '3', null, '104.192.74.23', '2019-02-20 06:46:34', '2019-02-20 14:46:34', '2019-02-20 14:46:34'), ('288', '2', null, '104.192.74.23', '2019-02-20 06:46:38', '2019-02-20 14:46:37', '2019-02-20 14:46:37'), ('289', '2', null, '104.192.74.23', '2019-02-20 06:47:00', '2019-02-20 14:46:59', '2019-02-20 14:46:59'), ('290', '9', null, '115.200.243.177', '2019-02-20 06:47:14', '2019-02-20 14:47:14', '2019-02-20 14:47:14'), ('291', '8', null, '115.200.243.177', '2019-02-20 06:47:25', '2019-02-20 14:47:24', '2019-02-20 14:47:24'), ('292', '9', null, '59.36.119.227', '2019-02-20 12:26:53', '2019-02-20 20:26:52', '2019-02-20 20:26:52'), ('293', '9', null, '104.192.74.38', '2019-02-21 07:24:21', '2019-02-21 15:24:20', '2019-02-21 15:24:20'), ('294', '8', null, '104.192.74.38', '2019-02-21 07:24:21', '2019-02-21 15:24:21', '2019-02-21 15:24:21'), ('295', '7', null, '104.192.74.38', '2019-02-21 07:24:27', '2019-02-21 15:24:27', '2019-02-21 15:24:27'), ('296', '7', null, '104.192.74.38', '2019-02-21 07:24:34', '2019-02-21 15:24:34', '2019-02-21 15:24:34'), ('297', '8', null, '104.192.74.38', '2019-02-21 07:24:36', '2019-02-21 15:24:36', '2019-02-21 15:24:36'), ('298', '9', null, '104.192.74.38', '2019-02-21 07:24:40', '2019-02-21 15:24:40', '2019-02-21 15:24:40'), ('299', '6', null, '104.192.74.38', '2019-02-21 07:24:44', '2019-02-21 15:24:43', '2019-02-21 15:24:43'), ('300', '5', null, '104.192.74.38', '2019-02-21 07:24:54', '2019-02-21 15:24:54', '2019-02-21 15:24:54'), ('301', '6', null, '104.192.74.38', '2019-02-21 07:24:55', '2019-02-21 15:24:54', '2019-02-21 15:24:54'), ('302', '4', null, '104.192.74.38', '2019-02-21 07:24:56', '2019-02-21 15:24:55', '2019-02-21 15:24:55'), ('303', '5', null, '104.192.74.38', '2019-02-21 07:25:08', '2019-02-21 15:25:08', '2019-02-21 15:25:08'), ('304', '3', null, '104.192.74.38', '2019-02-21 07:25:12', '2019-02-21 15:25:12', '2019-02-21 15:25:12'), ('305', '4', null, '104.192.74.38', '2019-02-21 07:25:12', '2019-02-21 15:25:12', '2019-02-21 15:25:12'), ('306', '2', null, '104.192.74.38', '2019-02-21 07:25:20', '2019-02-21 15:25:19', '2019-02-21 15:25:19'), ('307', '3', null, '104.192.74.38', '2019-02-21 07:25:33', '2019-02-21 15:25:32', '2019-02-21 15:25:32'), ('308', '2', null, '104.192.74.38', '2019-02-21 07:25:39', '2019-02-21 15:25:38', '2019-02-21 15:25:38'), ('309', '9', null, '115.200.243.177', '2019-02-21 07:40:04', '2019-02-21 15:40:03', '2019-02-21 15:40:03'), ('310', '9', null, '115.200.243.177', '2019-02-21 07:40:36', '2019-02-21 15:40:35', '2019-02-21 15:40:35'), ('311', '9', null, '61.151.178.197', '2019-02-21 07:41:37', '2019-02-21 15:41:36', '2019-02-21 15:41:36'), ('312', '9', null, '115.200.243.177', '2019-02-21 08:06:06', '2019-02-21 16:06:06', '2019-02-21 16:06:06'), ('313', '7', null, '115.200.243.177', '2019-02-21 08:06:24', '2019-02-21 16:06:24', '2019-02-21 16:06:24'), ('314', '7', null, '61.151.178.197', '2019-02-21 08:07:23', '2019-02-21 16:07:23', '2019-02-21 16:07:23'), ('315', '5', null, '58.251.121.185', '2019-02-21 11:55:11', '2019-02-21 19:55:11', '2019-02-21 19:55:11'), ('316', '9', null, '104.192.74.21', '2019-02-22 08:55:05', '2019-02-22 16:55:05', '2019-02-22 16:55:05'), ('317', '9', null, '104.192.74.21', '2019-02-22 08:55:16', '2019-02-22 16:55:15', '2019-02-22 16:55:15'), ('318', '8', null, '104.192.74.21', '2019-02-22 08:55:16', '2019-02-22 16:55:16', '2019-02-22 16:55:16'), ('319', '6', null, '104.192.74.21', '2019-02-22 08:55:21', '2019-02-22 16:55:20', '2019-02-22 16:55:20'), ('320', '7', null, '104.192.74.21', '2019-02-22 08:55:21', '2019-02-22 16:55:21', '2019-02-22 16:55:21'), ('321', '6', null, '104.192.74.21', '2019-02-22 08:55:34', '2019-02-22 16:55:34', '2019-02-22 16:55:34'), ('322', '7', null, '104.192.74.21', '2019-02-22 08:55:35', '2019-02-22 16:55:34', '2019-02-22 16:55:34'), ('323', '8', null, '104.192.74.21', '2019-02-22 08:55:37', '2019-02-22 16:55:37', '2019-02-22 16:55:37'), ('324', '5', null, '104.192.74.21', '2019-02-22 08:55:44', '2019-02-22 16:55:44', '2019-02-22 16:55:44'), ('325', '5', null, '104.192.74.21', '2019-02-22 08:55:57', '2019-02-22 16:55:57', '2019-02-22 16:55:57'), ('326', '4', null, '104.192.74.21', '2019-02-22 08:55:57', '2019-02-22 16:55:57', '2019-02-22 16:55:57'), ('327', '3', null, '104.192.74.21', '2019-02-22 08:55:57', '2019-02-22 16:55:57', '2019-02-22 16:55:57'), ('328', '4', null, '104.192.74.21', '2019-02-22 08:56:11', '2019-02-22 16:56:10', '2019-02-22 16:56:10'), ('329', '3', null, '104.192.74.21', '2019-02-22 08:56:14', '2019-02-22 16:56:14', '2019-02-22 16:56:14'), ('330', '2', null, '104.192.74.21', '2019-02-22 08:56:17', '2019-02-22 16:56:16', '2019-02-22 16:56:16'), ('331', '2', null, '104.192.74.21', '2019-02-22 08:56:31', '2019-02-22 16:56:31', '2019-02-22 16:56:31'), ('332', '9', null, '104.192.74.25', '2019-02-23 16:00:32', '2019-02-24 00:00:32', '2019-02-24 00:00:32'), ('333', '8', null, '104.192.74.25', '2019-02-23 16:00:35', '2019-02-24 00:00:35', '2019-02-24 00:00:35'), ('334', '9', null, '104.192.74.25', '2019-02-23 16:00:43', '2019-02-24 00:00:42', '2019-02-24 00:00:42'), ('335', '8', null, '104.192.74.25', '2019-02-23 16:00:54', '2019-02-24 00:00:54', '2019-02-24 00:00:54'), ('336', '7', null, '104.192.74.25', '2019-02-23 16:00:56', '2019-02-24 00:00:55', '2019-02-24 00:00:55'), ('337', '6', null, '104.192.74.25', '2019-02-23 16:00:58', '2019-02-24 00:00:58', '2019-02-24 00:00:58'), ('338', '7', null, '104.192.74.25', '2019-02-23 16:01:08', '2019-02-24 00:01:08', '2019-02-24 00:01:08'), ('339', '5', null, '104.192.74.25', '2019-02-23 16:01:14', '2019-02-24 00:01:14', '2019-02-24 00:01:14'), ('340', '4', null, '104.192.74.25', '2019-02-23 16:01:15', '2019-02-24 00:01:14', '2019-02-24 00:01:14'), ('341', '6', null, '104.192.74.25', '2019-02-23 16:01:16', '2019-02-24 00:01:16', '2019-02-24 00:01:16'), ('342', '4', null, '104.192.74.25', '2019-02-23 16:01:25', '2019-02-24 00:01:25', '2019-02-24 00:01:25'), ('343', '5', null, '104.192.74.25', '2019-02-23 16:01:32', '2019-02-24 00:01:32', '2019-02-24 00:01:32'), ('344', '3', null, '104.192.74.25', '2019-02-23 16:01:32', '2019-02-24 00:01:32', '2019-02-24 00:01:32'), ('345', '2', null, '104.192.74.25', '2019-02-23 16:01:38', '2019-02-24 00:01:37', '2019-02-24 00:01:37'), ('346', '3', null, '104.192.74.25', '2019-02-23 16:01:52', '2019-02-24 00:01:51', '2019-02-24 00:01:51'), ('347', '2', null, '104.192.74.25', '2019-02-23 16:01:54', '2019-02-24 00:01:53', '2019-02-24 00:01:53'), ('348', '9', null, '104.192.74.29', '2019-02-24 09:15:12', '2019-02-24 17:15:12', '2019-02-24 17:15:12'), ('349', '8', null, '104.192.74.29', '2019-02-24 09:15:13', '2019-02-24 17:15:12', '2019-02-24 17:15:12'), ('350', '7', null, '104.192.74.29', '2019-02-24 09:15:19', '2019-02-24 17:15:18', '2019-02-24 17:15:18'), ('351', '9', null, '104.192.74.29', '2019-02-24 09:15:27', '2019-02-24 17:15:26', '2019-02-24 17:15:26'), ('352', '7', null, '104.192.74.29', '2019-02-24 09:15:29', '2019-02-24 17:15:29', '2019-02-24 17:15:29'), ('353', '8', null, '104.192.74.29', '2019-02-24 09:15:30', '2019-02-24 17:15:30', '2019-02-24 17:15:30'), ('354', '6', null, '104.192.74.29', '2019-02-24 09:15:36', '2019-02-24 17:15:36', '2019-02-24 17:15:36'), ('355', '5', null, '104.192.74.29', '2019-02-24 09:15:42', '2019-02-24 17:15:42', '2019-02-24 17:15:42'), ('356', '6', null, '104.192.74.29', '2019-02-24 09:15:51', '2019-02-24 17:15:50', '2019-02-24 17:15:50'), ('357', '4', null, '104.192.74.29', '2019-02-24 09:15:54', '2019-02-24 17:15:53', '2019-02-24 17:15:53'), ('358', '5', null, '104.192.74.29', '2019-02-24 09:15:54', '2019-02-24 17:15:54', '2019-02-24 17:15:54'), ('359', '4', null, '104.192.74.29', '2019-02-24 09:16:04', '2019-02-24 17:16:04', '2019-02-24 17:16:04'), ('360', '3', null, '104.192.74.29', '2019-02-24 09:16:08', '2019-02-24 17:16:08', '2019-02-24 17:16:08'), ('361', '2', null, '104.192.74.29', '2019-02-24 09:16:15', '2019-02-24 17:16:15', '2019-02-24 17:16:15'), ('362', '3', null, '104.192.74.29', '2019-02-24 09:16:30', '2019-02-24 17:16:30', '2019-02-24 17:16:30'), ('363', '2', null, '104.192.74.29', '2019-02-24 09:16:31', '2019-02-24 17:16:31', '2019-02-24 17:16:31'), ('364', '9', null, '104.192.74.42', '2019-02-25 16:34:50', '2019-02-26 00:34:49', '2019-02-26 00:34:49'), ('365', '8', null, '104.192.74.42', '2019-02-25 16:34:53', '2019-02-26 00:34:52', '2019-02-26 00:34:52'), ('366', '9', null, '104.192.74.42', '2019-02-25 16:35:02', '2019-02-26 00:35:01', '2019-02-26 00:35:01'), ('367', '7', null, '104.192.74.42', '2019-02-25 16:35:05', '2019-02-26 00:35:04', '2019-02-26 00:35:04'), ('368', '7', null, '104.192.74.42', '2019-02-25 16:35:11', '2019-02-26 00:35:11', '2019-02-26 00:35:11'), ('369', '6', null, '104.192.74.42', '2019-02-25 16:35:12', '2019-02-26 00:35:12', '2019-02-26 00:35:12'), ('370', '8', null, '104.192.74.42', '2019-02-25 16:35:14', '2019-02-26 00:35:14', '2019-02-26 00:35:14'), ('371', '5', null, '104.192.74.42', '2019-02-25 16:35:25', '2019-02-26 00:35:24', '2019-02-26 00:35:24'), ('372', '6', null, '104.192.74.42', '2019-02-25 16:35:29', '2019-02-26 00:35:29', '2019-02-26 00:35:29'), ('373', '4', null, '104.192.74.42', '2019-02-25 16:35:37', '2019-02-26 00:35:36', '2019-02-26 00:35:36'), ('374', '3', null, '104.192.74.42', '2019-02-25 16:35:40', '2019-02-26 00:35:39', '2019-02-26 00:35:39'), ('375', '4', null, '104.192.74.42', '2019-02-25 16:35:48', '2019-02-26 00:35:47', '2019-02-26 00:35:47'), ('376', '5', null, '104.192.74.42', '2019-02-25 16:35:48', '2019-02-26 00:35:48', '2019-02-26 00:35:48'), ('377', '3', null, '104.192.74.42', '2019-02-25 16:35:53', '2019-02-26 00:35:52', '2019-02-26 00:35:52'), ('378', '2', null, '104.192.74.42', '2019-02-25 16:35:54', '2019-02-26 00:35:54', '2019-02-26 00:35:54'), ('379', '2', null, '104.192.74.42', '2019-02-25 16:36:10', '2019-02-26 00:36:10', '2019-02-26 00:36:10'), ('380', '9', null, '104.192.74.38', '2019-02-26 16:37:23', '2019-02-27 00:37:22', '2019-02-27 00:37:22'), ('381', '8', null, '104.192.74.38', '2019-02-26 16:37:27', '2019-02-27 00:37:27', '2019-02-27 00:37:27'), ('382', '9', null, '104.192.74.38', '2019-02-26 16:37:43', '2019-02-27 00:37:42', '2019-02-27 00:37:42'), ('383', '8', null, '104.192.74.38', '2019-02-26 16:37:43', '2019-02-27 00:37:42', '2019-02-27 00:37:42'), ('384', '7', null, '104.192.74.38', '2019-02-26 16:37:58', '2019-02-27 00:37:57', '2019-02-27 00:37:57'), ('385', '6', null, '104.192.74.38', '2019-02-26 16:37:59', '2019-02-27 00:37:59', '2019-02-27 00:37:59'), ('386', '7', null, '104.192.74.38', '2019-02-26 16:38:02', '2019-02-27 00:38:01', '2019-02-27 00:38:01'), ('387', '5', null, '104.192.74.38', '2019-02-26 16:38:10', '2019-02-27 00:38:10', '2019-02-27 00:38:10'), ('388', '4', null, '104.192.74.38', '2019-02-26 16:38:12', '2019-02-27 00:38:11', '2019-02-27 00:38:11'), ('389', '6', null, '104.192.74.38', '2019-02-26 16:38:15', '2019-02-27 00:38:14', '2019-02-27 00:38:14'), ('390', '3', null, '104.192.74.38', '2019-02-26 16:38:26', '2019-02-27 00:38:26', '2019-02-27 00:38:26'), ('391', '5', null, '104.192.74.38', '2019-02-26 16:38:32', '2019-02-27 00:38:32', '2019-02-27 00:38:32'), ('392', '2', null, '104.192.74.38', '2019-02-26 16:38:40', '2019-02-27 00:38:40', '2019-02-27 00:38:40'), ('393', '3', null, '104.192.74.38', '2019-02-26 16:38:43', '2019-02-27 00:38:42', '2019-02-27 00:38:42'), ('394', '4', null, '104.192.74.38', '2019-02-26 16:38:49', '2019-02-27 00:38:48', '2019-02-27 00:38:48'), ('395', '2', null, '104.192.74.38', '2019-02-26 16:38:58', '2019-02-27 00:38:57', '2019-02-27 00:38:57'), ('396', '9', null, '104.192.74.43', '2019-02-27 07:15:58', '2019-02-27 15:15:57', '2019-02-27 15:15:57'), ('397', '9', null, '104.192.74.43', '2019-02-27 07:16:03', '2019-02-27 15:16:03', '2019-02-27 15:16:03'), ('398', '8', null, '104.192.74.43', '2019-02-27 07:16:03', '2019-02-27 15:16:03', '2019-02-27 15:16:03'), ('399', '7', null, '104.192.74.43', '2019-02-27 07:16:04', '2019-02-27 15:16:03', '2019-02-27 15:16:03'), ('400', '7', null, '104.192.74.43', '2019-02-27 07:16:16', '2019-02-27 15:16:16', '2019-02-27 15:16:16'), ('401', '6', null, '104.192.74.43', '2019-02-27 07:16:16', '2019-02-27 15:16:16', '2019-02-27 15:16:16'), ('402', '8', null, '104.192.74.43', '2019-02-27 07:16:20', '2019-02-27 15:16:19', '2019-02-27 15:16:19'), ('403', '5', null, '104.192.74.43', '2019-02-27 07:16:31', '2019-02-27 15:16:30', '2019-02-27 15:16:30'), ('404', '6', null, '104.192.74.43', '2019-02-27 07:16:33', '2019-02-27 15:16:32', '2019-02-27 15:16:32'), ('405', '4', null, '104.192.74.43', '2019-02-27 07:16:40', '2019-02-27 15:16:40', '2019-02-27 15:16:40'), ('406', '5', null, '104.192.74.43', '2019-02-27 07:16:47', '2019-02-27 15:16:47', '2019-02-27 15:16:47'), ('407', '4', null, '104.192.74.43', '2019-02-27 07:16:47', '2019-02-27 15:16:47', '2019-02-27 15:16:47'), ('408', '3', null, '104.192.74.43', '2019-02-27 07:16:47', '2019-02-27 15:16:47', '2019-02-27 15:16:47'), ('409', '2', null, '104.192.74.43', '2019-02-27 07:16:55', '2019-02-27 15:16:54', '2019-02-27 15:16:54'), ('410', '3', null, '104.192.74.43', '2019-02-27 07:17:08', '2019-02-27 15:17:08', '2019-02-27 15:17:08'), ('411', '2', null, '104.192.74.43', '2019-02-27 07:17:09', '2019-02-27 15:17:09', '2019-02-27 15:17:09'), ('412', '9', null, '104.192.74.36', '2019-02-28 06:49:36', '2019-02-28 14:49:35', '2019-02-28 14:49:35'), ('413', '9', null, '104.192.74.36', '2019-02-28 06:49:44', '2019-02-28 14:49:43', '2019-02-28 14:49:43'), ('414', '8', null, '104.192.74.36', '2019-02-28 06:49:44', '2019-02-28 14:49:44', '2019-02-28 14:49:44'), ('415', '7', null, '104.192.74.36', '2019-02-28 06:49:45', '2019-02-28 14:49:45', '2019-02-28 14:49:45'), ('416', '6', null, '104.192.74.36', '2019-02-28 06:49:51', '2019-02-28 14:49:51', '2019-02-28 14:49:51'), ('417', '7', null, '104.192.74.36', '2019-02-28 06:50:01', '2019-02-28 14:50:00', '2019-02-28 14:50:00'), ('418', '8', null, '104.192.74.36', '2019-02-28 06:50:06', '2019-02-28 14:50:05', '2019-02-28 14:50:05'), ('419', '6', null, '104.192.74.36', '2019-02-28 06:50:06', '2019-02-28 14:50:05', '2019-02-28 14:50:05'), ('420', '5', null, '104.192.74.36', '2019-02-28 06:50:07', '2019-02-28 14:50:06', '2019-02-28 14:50:06'), ('421', '4', null, '104.192.74.36', '2019-02-28 06:50:22', '2019-02-28 14:50:22', '2019-02-28 14:50:22'), ('422', '5', null, '104.192.74.36', '2019-02-28 06:50:27', '2019-02-28 14:50:27', '2019-02-28 14:50:27'), ('423', '3', null, '104.192.74.36', '2019-02-28 06:50:29', '2019-02-28 14:50:28', '2019-02-28 14:50:28'), ('424', '4', null, '104.192.74.36', '2019-02-28 06:50:30', '2019-02-28 14:50:29', '2019-02-28 14:50:29'), ('425', '2', null, '104.192.74.36', '2019-02-28 06:50:42', '2019-02-28 14:50:41', '2019-02-28 14:50:41'), ('426', '3', null, '104.192.74.36', '2019-02-28 06:50:46', '2019-02-28 14:50:45', '2019-02-28 14:50:45'), ('427', '2', null, '104.192.74.36', '2019-02-28 06:50:59', '2019-02-28 14:50:59', '2019-02-28 14:50:59'), ('428', '9', null, '104.192.74.42', '2019-03-01 14:55:55', '2019-03-01 22:55:55', '2019-03-01 22:55:55'), ('429', '8', null, '104.192.74.42', '2019-03-01 14:55:57', '2019-03-01 22:55:57', '2019-03-01 22:55:57'), ('430', '9', null, '104.192.74.42', '2019-03-01 14:56:02', '2019-03-01 22:56:02', '2019-03-01 22:56:02'), ('431', '7', null, '104.192.74.42', '2019-03-01 14:56:03', '2019-03-01 22:56:02', '2019-03-01 22:56:02'), ('432', '6', null, '104.192.74.42', '2019-03-01 14:56:11', '2019-03-01 22:56:11', '2019-03-01 22:56:11'), ('433', '7', null, '104.192.74.42', '2019-03-01 14:56:12', '2019-03-01 22:56:11', '2019-03-01 22:56:11'), ('434', '8', null, '104.192.74.42', '2019-03-01 14:56:18', '2019-03-01 22:56:18', '2019-03-01 22:56:18'), ('435', '6', null, '104.192.74.42', '2019-03-01 14:56:25', '2019-03-01 22:56:25', '2019-03-01 22:56:25'), ('436', '5', null, '104.192.74.42', '2019-03-01 14:56:32', '2019-03-01 22:56:32', '2019-03-01 22:56:32'), ('437', '4', null, '104.192.74.42', '2019-03-01 14:56:32', '2019-03-01 22:56:32', '2019-03-01 22:56:32'), ('438', '3', null, '104.192.74.42', '2019-03-01 14:56:38', '2019-03-01 22:56:37', '2019-03-01 22:56:37'), ('439', '4', null, '104.192.74.42', '2019-03-01 14:56:43', '2019-03-01 22:56:42', '2019-03-01 22:56:42'), ('440', '5', null, '104.192.74.42', '2019-03-01 14:56:52', '2019-03-01 22:56:51', '2019-03-01 22:56:51'), ('441', '2', null, '104.192.74.42', '2019-03-01 14:56:53', '2019-03-01 22:56:53', '2019-03-01 22:56:53'), ('442', '3', null, '104.192.74.42', '2019-03-01 14:56:55', '2019-03-01 22:56:55', '2019-03-01 22:56:55'), ('443', '2', null, '104.192.74.42', '2019-03-01 14:57:10', '2019-03-01 22:57:10', '2019-03-01 22:57:10'), ('444', '9', null, '104.192.74.36', '2019-03-02 13:02:02', '2019-03-02 21:02:02', '2019-03-02 21:02:02'), ('445', '8', null, '104.192.74.36', '2019-03-02 13:02:09', '2019-03-02 21:02:09', '2019-03-02 21:02:09'), ('446', '9', null, '104.192.74.36', '2019-03-02 13:02:14', '2019-03-02 21:02:14', '2019-03-02 21:02:14'), ('447', '7', null, '104.192.74.36', '2019-03-02 13:02:22', '2019-03-02 21:02:21', '2019-03-02 21:02:21'), ('448', '7', null, '104.192.74.36', '2019-03-02 13:02:26', '2019-03-02 21:02:26', '2019-03-02 21:02:26'), ('449', '6', null, '104.192.74.36', '2019-03-02 13:02:29', '2019-03-02 21:02:29', '2019-03-02 21:02:29'), ('450', '8', null, '104.192.74.36', '2019-03-02 13:02:30', '2019-03-02 21:02:30', '2019-03-02 21:02:30'), ('451', '5', null, '104.192.74.36', '2019-03-02 13:02:34', '2019-03-02 21:02:34', '2019-03-02 21:02:34'), ('452', '6', null, '104.192.74.36', '2019-03-02 13:02:44', '2019-03-02 21:02:44', '2019-03-02 21:02:44'), ('453', '5', null, '104.192.74.36', '2019-03-02 13:02:54', '2019-03-02 21:02:54', '2019-03-02 21:02:54'), ('454', '4', null, '104.192.74.36', '2019-03-02 13:02:56', '2019-03-02 21:02:55', '2019-03-02 21:02:55'), ('455', '3', null, '104.192.74.36', '2019-03-02 13:02:56', '2019-03-02 21:02:56', '2019-03-02 21:02:56'), ('456', '3', null, '104.192.74.36', '2019-03-02 13:03:11', '2019-03-02 21:03:11', '2019-03-02 21:03:11'), ('457', '4', null, '104.192.74.36', '2019-03-02 13:03:11', '2019-03-02 21:03:11', '2019-03-02 21:03:11'), ('458', '2', null, '104.192.74.36', '2019-03-02 13:03:13', '2019-03-02 21:03:13', '2019-03-02 21:03:13'), ('459', '2', null, '104.192.74.36', '2019-03-02 13:03:27', '2019-03-02 21:03:26', '2019-03-02 21:03:26'), ('460', '7', null, '178.62.82.141', '2019-03-03 07:11:43', '2019-03-03 15:11:42', '2019-03-03 15:11:42'), ('461', '6', null, '178.62.82.141', '2019-03-03 07:11:47', '2019-03-03 15:11:46', '2019-03-03 15:11:46'), ('462', '5', null, '178.62.82.141', '2019-03-03 07:11:53', '2019-03-03 15:11:53', '2019-03-03 15:11:53'), ('463', '8', null, '178.62.82.141', '2019-03-03 07:12:01', '2019-03-03 15:12:00', '2019-03-03 15:12:00'), ('464', '7', null, '101.226.114.193', '2019-03-03 11:17:05', '2019-03-03 19:17:04', '2019-03-03 19:17:04'), ('465', '9', null, '104.192.74.42', '2019-03-03 16:40:38', '2019-03-04 00:40:37', '2019-03-04 00:40:37'), ('466', '8', null, '104.192.74.42', '2019-03-03 16:40:45', '2019-03-04 00:40:44', '2019-03-04 00:40:44'), ('467', '9', null, '104.192.74.42', '2019-03-03 16:40:48', '2019-03-04 00:40:47', '2019-03-04 00:40:47'), ('468', '7', null, '104.192.74.42', '2019-03-03 16:40:55', '2019-03-04 00:40:54', '2019-03-04 00:40:54'), ('469', '6', null, '104.192.74.42', '2019-03-03 16:40:58', '2019-03-04 00:40:58', '2019-03-04 00:40:58'), ('470', '7', null, '104.192.74.42', '2019-03-03 16:40:59', '2019-03-04 00:40:58', '2019-03-04 00:40:58'), ('471', '8', null, '104.192.74.42', '2019-03-03 16:41:03', '2019-03-04 00:41:03', '2019-03-04 00:41:03'), ('472', '5', null, '104.192.74.42', '2019-03-03 16:41:12', '2019-03-04 00:41:12', '2019-03-04 00:41:12'), ('473', '6', null, '104.192.74.42', '2019-03-03 16:41:20', '2019-03-04 00:41:19', '2019-03-04 00:41:19'), ('474', '5', null, '104.192.74.42', '2019-03-03 16:41:26', '2019-03-04 00:41:26', '2019-03-04 00:41:26'), ('475', '3', null, '104.192.74.42', '2019-03-03 16:41:29', '2019-03-04 00:41:28', '2019-03-04 00:41:28'), ('476', '4', null, '104.192.74.42', '2019-03-03 16:41:29', '2019-03-04 00:41:29', '2019-03-04 00:41:29'), ('477', '2', null, '104.192.74.42', '2019-03-03 16:41:42', '2019-03-04 00:41:42', '2019-03-04 00:41:42'), ('478', '4', null, '104.192.74.42', '2019-03-03 16:41:47', '2019-03-04 00:41:47', '2019-03-04 00:41:47'), ('479', '3', null, '104.192.74.42', '2019-03-03 16:41:49', '2019-03-04 00:41:49', '2019-03-04 00:41:49'), ('480', '2', null, '104.192.74.42', '2019-03-03 16:41:59', '2019-03-04 00:41:58', '2019-03-04 00:41:58'), ('481', '9', null, '104.192.74.23', '2019-03-04 10:02:46', '2019-03-04 18:02:46', '2019-03-04 18:02:46'), ('482', '8', null, '104.192.74.23', '2019-03-04 10:02:56', '2019-03-04 18:02:56', '2019-03-04 18:02:56'), ('483', '9', null, '104.192.74.23', '2019-03-04 10:02:56', '2019-03-04 18:02:56', '2019-03-04 18:02:56'), ('484', '7', null, '104.192.74.23', '2019-03-04 10:02:57', '2019-03-04 18:02:57', '2019-03-04 18:02:57'), ('485', '6', null, '104.192.74.23', '2019-03-04 10:03:08', '2019-03-04 18:03:08', '2019-03-04 18:03:08'), ('486', '7', null, '104.192.74.23', '2019-03-04 10:03:14', '2019-03-04 18:03:13', '2019-03-04 18:03:13'), ('487', '8', null, '104.192.74.23', '2019-03-04 10:03:16', '2019-03-04 18:03:16', '2019-03-04 18:03:16'), ('488', '6', null, '104.192.74.23', '2019-03-04 10:03:22', '2019-03-04 18:03:22', '2019-03-04 18:03:22'), ('489', '5', null, '104.192.74.23', '2019-03-04 10:03:23', '2019-03-04 18:03:22', '2019-03-04 18:03:22'), ('490', '4', null, '104.192.74.23', '2019-03-04 10:03:37', '2019-03-04 18:03:36', '2019-03-04 18:03:36'), ('491', '5', null, '104.192.74.23', '2019-03-04 10:03:40', '2019-03-04 18:03:39', '2019-03-04 18:03:39'), ('492', '3', null, '104.192.74.23', '2019-03-04 10:03:41', '2019-03-04 18:03:40', '2019-03-04 18:03:40'), ('493', '4', null, '104.192.74.23', '2019-03-04 10:03:42', '2019-03-04 18:03:41', '2019-03-04 18:03:41'), ('494', '2', null, '104.192.74.23', '2019-03-04 10:03:53', '2019-03-04 18:03:52', '2019-03-04 18:03:52'), ('495', '3', null, '104.192.74.23', '2019-03-04 10:04:02', '2019-03-04 18:04:01', '2019-03-04 18:04:01'), ('496', '2', null, '104.192.74.23', '2019-03-04 10:04:04', '2019-03-04 18:04:03', '2019-03-04 18:04:03'), ('497', '9', null, '104.192.74.22', '2019-03-05 06:49:35', '2019-03-05 14:49:35', '2019-03-05 14:49:35'), ('498', '8', null, '104.192.74.22', '2019-03-05 06:49:40', '2019-03-05 14:49:40', '2019-03-05 14:49:40'), ('499', '9', null, '104.192.74.22', '2019-03-05 06:49:46', '2019-03-05 14:49:46', '2019-03-05 14:49:46'), ('500', '7', null, '104.192.74.22', '2019-03-05 06:49:56', '2019-03-05 14:49:56', '2019-03-05 14:49:56'), ('501', '7', null, '104.192.74.22', '2019-03-05 06:50:01', '2019-03-05 14:50:00', '2019-03-05 14:50:00'), ('502', '6', null, '104.192.74.22', '2019-03-05 06:50:01', '2019-03-05 14:50:00', '2019-03-05 14:50:00'), ('503', '8', null, '104.192.74.22', '2019-03-05 06:50:05', '2019-03-05 14:50:04', '2019-03-05 14:50:04'), ('504', '6', null, '104.192.74.22', '2019-03-05 06:50:18', '2019-03-05 14:50:17', '2019-03-05 14:50:17'), ('505', '5', null, '104.192.74.22', '2019-03-05 06:50:19', '2019-03-05 14:50:18', '2019-03-05 14:50:18'), ('506', '4', null, '104.192.74.22', '2019-03-05 06:50:20', '2019-03-05 14:50:19', '2019-03-05 14:50:19'), ('507', '3', null, '104.192.74.22', '2019-03-05 06:50:34', '2019-03-05 14:50:33', '2019-03-05 14:50:33'), ('508', '4', null, '104.192.74.22', '2019-03-05 06:50:40', '2019-03-05 14:50:39', '2019-03-05 14:50:39'), ('509', '3', null, '104.192.74.22', '2019-03-05 06:50:43', '2019-03-05 14:50:43', '2019-03-05 14:50:43'), ('510', '5', null, '104.192.74.22', '2019-03-05 06:50:44', '2019-03-05 14:50:43', '2019-03-05 14:50:43'), ('511', '2', null, '104.192.74.22', '2019-03-05 06:50:45', '2019-03-05 14:50:45', '2019-03-05 14:50:45'), ('512', '2', null, '104.192.74.22', '2019-03-05 06:51:02', '2019-03-05 14:51:01', '2019-03-05 14:51:01'), ('513', '9', null, '104.192.74.37', '2019-03-06 09:52:41', '2019-03-06 17:52:40', '2019-03-06 17:52:40'), ('514', '9', null, '104.192.74.37', '2019-03-06 09:52:50', '2019-03-06 17:52:50', '2019-03-06 17:52:50'), ('515', '8', null, '104.192.74.37', '2019-03-06 09:52:50', '2019-03-06 17:52:50', '2019-03-06 17:52:50'), ('516', '7', null, '104.192.74.37', '2019-03-06 09:52:55', '2019-03-06 17:52:55', '2019-03-06 17:52:55'), ('517', '7', null, '104.192.74.37', '2019-03-06 09:53:03', '2019-03-06 17:53:02', '2019-03-06 17:53:02'), ('518', '6', null, '104.192.74.37', '2019-03-06 09:53:03', '2019-03-06 17:53:03', '2019-03-06 17:53:03'), ('519', '8', null, '104.192.74.37', '2019-03-06 09:53:04', '2019-03-06 17:53:03', '2019-03-06 17:53:03'), ('520', '5', null, '104.192.74.37', '2019-03-06 09:53:13', '2019-03-06 17:53:13', '2019-03-06 17:53:13'), ('521', '6', null, '104.192.74.37', '2019-03-06 09:53:21', '2019-03-06 17:53:21', '2019-03-06 17:53:21'), ('522', '4', null, '104.192.74.37', '2019-03-06 09:53:23', '2019-03-06 17:53:22', '2019-03-06 17:53:22'), ('523', '3', null, '104.192.74.37', '2019-03-06 09:53:33', '2019-03-06 17:53:33', '2019-03-06 17:53:33'), ('524', '5', null, '104.192.74.37', '2019-03-06 09:53:35', '2019-03-06 17:53:35', '2019-03-06 17:53:35'), ('525', '4', null, '104.192.74.37', '2019-03-06 09:53:38', '2019-03-06 17:53:37', '2019-03-06 17:53:37'), ('526', '2', null, '104.192.74.37', '2019-03-06 09:53:47', '2019-03-06 17:53:47', '2019-03-06 17:53:47'), ('527', '3', null, '104.192.74.37', '2019-03-06 09:53:48', '2019-03-06 17:53:47', '2019-03-06 17:53:47'), ('528', '2', null, '104.192.74.37', '2019-03-06 09:54:08', '2019-03-06 17:54:08', '2019-03-06 17:54:08'), ('529', '9', null, '104.192.74.42', '2019-03-07 08:33:45', '2019-03-07 16:33:44', '2019-03-07 16:33:44'), ('530', '8', null, '104.192.74.42', '2019-03-07 08:33:53', '2019-03-07 16:33:52', '2019-03-07 16:33:52'), ('531', '9', null, '104.192.74.42', '2019-03-07 08:33:54', '2019-03-07 16:33:54', '2019-03-07 16:33:54'), ('532', '7', null, '104.192.74.42', '2019-03-07 08:33:55', '2019-03-07 16:33:54', '2019-03-07 16:33:54'), ('533', '7', null, '104.192.74.42', '2019-03-07 08:34:02', '2019-03-07 16:34:02', '2019-03-07 16:34:02'), ('534', '6', null, '104.192.74.42', '2019-03-07 08:34:08', '2019-03-07 16:34:07', '2019-03-07 16:34:07'), ('535', '5', null, '104.192.74.42', '2019-03-07 08:34:08', '2019-03-07 16:34:08', '2019-03-07 16:34:08'), ('536', '8', null, '104.192.74.42', '2019-03-07 08:34:14', '2019-03-07 16:34:14', '2019-03-07 16:34:14'), ('537', '6', null, '104.192.74.42', '2019-03-07 08:34:20', '2019-03-07 16:34:20', '2019-03-07 16:34:20'), ('538', '5', null, '104.192.74.42', '2019-03-07 08:34:30', '2019-03-07 16:34:30', '2019-03-07 16:34:30'), ('539', '4', null, '104.192.74.42', '2019-03-07 08:34:35', '2019-03-07 16:34:34', '2019-03-07 16:34:34'), ('540', '3', null, '104.192.74.42', '2019-03-07 08:34:41', '2019-03-07 16:34:41', '2019-03-07 16:34:41'), ('541', '2', null, '104.192.74.42', '2019-03-07 08:34:42', '2019-03-07 16:34:42', '2019-03-07 16:34:42'), ('542', '4', null, '104.192.74.42', '2019-03-07 08:34:44', '2019-03-07 16:34:44', '2019-03-07 16:34:44'), ('543', '2', null, '104.192.74.42', '2019-03-07 08:35:01', '2019-03-07 16:35:00', '2019-03-07 16:35:00'), ('544', '3', null, '104.192.74.42', '2019-03-07 08:35:01', '2019-03-07 16:35:01', '2019-03-07 16:35:01'), ('545', '9', null, '104.192.74.16', '2019-03-08 14:09:53', '2019-03-08 22:09:53', '2019-03-08 22:09:53'), ('546', '8', null, '104.192.74.16', '2019-03-08 14:09:53', '2019-03-08 22:09:53', '2019-03-08 22:09:53'), ('547', '7', null, '104.192.74.16', '2019-03-08 14:09:57', '2019-03-08 22:09:57', '2019-03-08 22:09:57'), ('548', '9', null, '104.192.74.16', '2019-03-08 14:10:07', '2019-03-08 22:10:06', '2019-03-08 22:10:06'), ('549', '7', null, '104.192.74.16', '2019-03-08 14:10:09', '2019-03-08 22:10:09', '2019-03-08 22:10:09'), ('550', '6', null, '104.192.74.16', '2019-03-08 14:10:14', '2019-03-08 22:10:13', '2019-03-08 22:10:13'), ('551', '8', null, '104.192.74.16', '2019-03-08 14:10:15', '2019-03-08 22:10:14', '2019-03-08 22:10:14'), ('552', '5', null, '104.192.74.16', '2019-03-08 14:10:15', '2019-03-08 22:10:14', '2019-03-08 22:10:14'), ('553', '6', null, '104.192.74.16', '2019-03-08 14:10:33', '2019-03-08 22:10:33', '2019-03-08 22:10:33'), ('554', '5', null, '104.192.74.16', '2019-03-08 14:10:34', '2019-03-08 22:10:34', '2019-03-08 22:10:34'), ('555', '4', null, '104.192.74.16', '2019-03-08 14:10:35', '2019-03-08 22:10:34', '2019-03-08 22:10:34'), ('556', '4', null, '104.192.74.16', '2019-03-08 14:10:45', '2019-03-08 22:10:44', '2019-03-08 22:10:44'), ('557', '3', null, '104.192.74.16', '2019-03-08 14:10:51', '2019-03-08 22:10:50', '2019-03-08 22:10:50'), ('558', '2', null, '104.192.74.16', '2019-03-08 14:10:52', '2019-03-08 22:10:51', '2019-03-08 22:10:51'), ('559', '3', null, '104.192.74.16', '2019-03-08 14:11:05', '2019-03-08 22:11:04', '2019-03-08 22:11:04'), ('560', '2', null, '104.192.74.16', '2019-03-08 14:11:11', '2019-03-08 22:11:10', '2019-03-08 22:11:10'), ('561', '8', null, '163.177.90.152', '2019-03-08 18:43:10', '2019-03-09 02:43:10', '2019-03-09 02:43:10'), ('562', '9', null, '104.192.74.19', '2019-03-09 06:54:16', '2019-03-09 14:54:16', '2019-03-09 14:54:16'), ('563', '8', null, '104.192.74.19', '2019-03-09 06:54:24', '2019-03-09 14:54:24', '2019-03-09 14:54:24'), ('564', '7', null, '104.192.74.19', '2019-03-09 06:54:24', '2019-03-09 14:54:24', '2019-03-09 14:54:24'), ('565', '9', null, '104.192.74.19', '2019-03-09 06:54:26', '2019-03-09 14:54:26', '2019-03-09 14:54:26'), ('566', '6', null, '104.192.74.19', '2019-03-09 06:54:37', '2019-03-09 14:54:36', '2019-03-09 14:54:36'), ('567', '7', null, '104.192.74.19', '2019-03-09 06:54:39', '2019-03-09 14:54:38', '2019-03-09 14:54:38'), ('568', '8', null, '104.192.74.19', '2019-03-09 06:54:42', '2019-03-09 14:54:41', '2019-03-09 14:54:41'), ('569', '5', null, '104.192.74.19', '2019-03-09 06:54:47', '2019-03-09 14:54:47', '2019-03-09 14:54:47'), ('570', '6', null, '104.192.74.19', '2019-03-09 06:54:54', '2019-03-09 14:54:54', '2019-03-09 14:54:54'), ('571', '4', null, '104.192.74.19', '2019-03-09 06:55:04', '2019-03-09 14:55:04', '2019-03-09 14:55:04'), ('572', '5', null, '104.192.74.19', '2019-03-09 06:55:05', '2019-03-09 14:55:04', '2019-03-09 14:55:04'), ('573', '3', null, '104.192.74.19', '2019-03-09 06:55:10', '2019-03-09 14:55:09', '2019-03-09 14:55:09'), ('574', '4', null, '104.192.74.19', '2019-03-09 06:55:13', '2019-03-09 14:55:13', '2019-03-09 14:55:13'), ('575', '2', null, '104.192.74.19', '2019-03-09 06:55:23', '2019-03-09 14:55:23', '2019-03-09 14:55:23'), ('576', '3', null, '104.192.74.19', '2019-03-09 06:55:26', '2019-03-09 14:55:25', '2019-03-09 14:55:25'), ('577', '2', null, '104.192.74.19', '2019-03-09 06:55:37', '2019-03-09 14:55:37', '2019-03-09 14:55:37'), ('578', '9', null, '104.192.74.12', '2019-03-10 11:03:31', '2019-03-10 19:03:30', '2019-03-10 19:03:30'), ('579', '8', null, '104.192.74.12', '2019-03-10 11:03:35', '2019-03-10 19:03:34', '2019-03-10 19:03:34'), ('580', '9', null, '104.192.74.12', '2019-03-10 11:03:36', '2019-03-10 19:03:36', '2019-03-10 19:03:36'), ('581', '7', null, '104.192.74.12', '2019-03-10 11:03:40', '2019-03-10 19:03:39', '2019-03-10 19:03:39'), ('582', '7', null, '104.192.74.12', '2019-03-10 11:03:49', '2019-03-10 19:03:48', '2019-03-10 19:03:48'), ('583', '6', null, '104.192.74.12', '2019-03-10 11:03:50', '2019-03-10 19:03:49', '2019-03-10 19:03:49'), ('584', '5', null, '104.192.74.12', '2019-03-10 11:03:54', '2019-03-10 19:03:54', '2019-03-10 19:03:54'), ('585', '8', null, '104.192.74.12', '2019-03-10 11:03:55', '2019-03-10 19:03:54', '2019-03-10 19:03:54'), ('586', '6', null, '104.192.74.12', '2019-03-10 11:04:07', '2019-03-10 19:04:06', '2019-03-10 19:04:06'), ('587', '5', null, '104.192.74.12', '2019-03-10 11:04:13', '2019-03-10 19:04:12', '2019-03-10 19:04:12'), ('588', '4', null, '104.192.74.12', '2019-03-10 11:04:18', '2019-03-10 19:04:18', '2019-03-10 19:04:18'), ('589', '3', null, '104.192.74.12', '2019-03-10 11:04:21', '2019-03-10 19:04:21', '2019-03-10 19:04:21'), ('590', '4', null, '104.192.74.12', '2019-03-10 11:04:30', '2019-03-10 19:04:29', '2019-03-10 19:04:29'), ('591', '2', null, '104.192.74.12', '2019-03-10 11:04:34', '2019-03-10 19:04:33', '2019-03-10 19:04:33'), ('592', '3', null, '104.192.74.12', '2019-03-10 11:04:36', '2019-03-10 19:04:35', '2019-03-10 19:04:35'), ('593', '2', null, '104.192.74.12', '2019-03-10 11:04:54', '2019-03-10 19:04:53', '2019-03-10 19:04:53'), ('594', '9', null, '104.192.74.17', '2019-03-11 10:19:23', '2019-03-11 18:19:23', '2019-03-11 18:19:23'), ('595', '8', null, '104.192.74.17', '2019-03-11 10:19:24', '2019-03-11 18:19:23', '2019-03-11 18:19:23'), ('596', '7', null, '104.192.74.17', '2019-03-11 10:19:33', '2019-03-11 18:19:32', '2019-03-11 18:19:32'), ('597', '9', null, '104.192.74.17', '2019-03-11 10:19:37', '2019-03-11 18:19:36', '2019-03-11 18:19:36'), ('598', '6', null, '104.192.74.17', '2019-03-11 10:19:41', '2019-03-11 18:19:41', '2019-03-11 18:19:41'), ('599', '7', null, '104.192.74.17', '2019-03-11 10:19:41', '2019-03-11 18:19:41', '2019-03-11 18:19:41'), ('600', '8', null, '104.192.74.17', '2019-03-11 10:19:46', '2019-03-11 18:19:45', '2019-03-11 18:19:45'), ('601', '5', null, '104.192.74.17', '2019-03-11 10:19:54', '2019-03-11 18:19:53', '2019-03-11 18:19:53'), ('602', '6', null, '104.192.74.17', '2019-03-11 10:19:56', '2019-03-11 18:19:55', '2019-03-11 18:19:55'), ('603', '4', null, '104.192.74.17', '2019-03-11 10:20:04', '2019-03-11 18:20:04', '2019-03-11 18:20:04'), ('604', '3', null, '104.192.74.17', '2019-03-11 10:20:11', '2019-03-11 18:20:10', '2019-03-11 18:20:10'), ('605', '4', null, '104.192.74.17', '2019-03-11 10:20:15', '2019-03-11 18:20:14', '2019-03-11 18:20:14'), ('606', '5', null, '104.192.74.17', '2019-03-11 10:20:16', '2019-03-11 18:20:16', '2019-03-11 18:20:16'), ('607', '2', null, '104.192.74.17', '2019-03-11 10:20:25', '2019-03-11 18:20:25', '2019-03-11 18:20:25'), ('608', '3', null, '104.192.74.17', '2019-03-11 10:20:27', '2019-03-11 18:20:27', '2019-03-11 18:20:27'), ('609', '2', null, '104.192.74.17', '2019-03-11 10:20:42', '2019-03-11 18:20:42', '2019-03-11 18:20:42'), ('610', '9', null, '104.192.74.38', '2019-03-12 05:44:14', '2019-03-12 13:44:14', '2019-03-12 13:44:14'), ('611', '8', null, '104.192.74.38', '2019-03-12 05:44:16', '2019-03-12 13:44:15', '2019-03-12 13:44:15'), ('612', '7', null, '104.192.74.38', '2019-03-12 05:44:19', '2019-03-12 13:44:18', '2019-03-12 13:44:18'), ('613', '9', null, '104.192.74.38', '2019-03-12 05:44:27', '2019-03-12 13:44:26', '2019-03-12 13:44:26'), ('614', '7', null, '104.192.74.38', '2019-03-12 05:44:30', '2019-03-12 13:44:29', '2019-03-12 13:44:29'), ('615', '8', null, '104.192.74.38', '2019-03-12 05:44:31', '2019-03-12 13:44:31', '2019-03-12 13:44:31'), ('616', '6', null, '104.192.74.38', '2019-03-12 05:44:40', '2019-03-12 13:44:39', '2019-03-12 13:44:39'), ('617', '5', null, '104.192.74.38', '2019-03-12 05:44:41', '2019-03-12 13:44:41', '2019-03-12 13:44:41'), ('618', '4', null, '104.192.74.38', '2019-03-12 05:44:45', '2019-03-12 13:44:45', '2019-03-12 13:44:45');
INSERT INTO `biz_article_look` VALUES ('619', '4', null, '104.192.74.38', '2019-03-12 05:44:56', '2019-03-12 13:44:56', '2019-03-12 13:44:56'), ('620', '6', null, '104.192.74.38', '2019-03-12 05:44:56', '2019-03-12 13:44:56', '2019-03-12 13:44:56'), ('621', '5', null, '104.192.74.38', '2019-03-12 05:45:02', '2019-03-12 13:45:02', '2019-03-12 13:45:02'), ('622', '3', null, '104.192.74.38', '2019-03-12 05:45:10', '2019-03-12 13:45:09', '2019-03-12 13:45:09'), ('623', '2', null, '104.192.74.38', '2019-03-12 05:45:10', '2019-03-12 13:45:09', '2019-03-12 13:45:09'), ('624', '3', null, '104.192.74.38', '2019-03-12 05:45:24', '2019-03-12 13:45:24', '2019-03-12 13:45:24'), ('625', '2', null, '104.192.74.38', '2019-03-12 05:45:25', '2019-03-12 13:45:25', '2019-03-12 13:45:25'), ('626', '9', null, '104.192.74.35', '2019-03-13 11:23:01', '2019-03-13 19:23:00', '2019-03-13 19:23:00'), ('627', '8', null, '104.192.74.35', '2019-03-13 11:23:15', '2019-03-13 19:23:14', '2019-03-13 19:23:14'), ('628', '7', null, '104.192.74.35', '2019-03-13 11:23:16', '2019-03-13 19:23:15', '2019-03-13 19:23:15'), ('629', '9', null, '104.192.74.35', '2019-03-13 11:23:16', '2019-03-13 19:23:15', '2019-03-13 19:23:15'), ('630', '7', null, '104.192.74.35', '2019-03-13 11:23:27', '2019-03-13 19:23:26', '2019-03-13 19:23:26'), ('631', '6', null, '104.192.74.35', '2019-03-13 11:23:29', '2019-03-13 19:23:29', '2019-03-13 19:23:29'), ('632', '8', null, '104.192.74.35', '2019-03-13 11:23:30', '2019-03-13 19:23:30', '2019-03-13 19:23:30'), ('633', '5', null, '104.192.74.35', '2019-03-13 11:23:34', '2019-03-13 19:23:34', '2019-03-13 19:23:34'), ('634', '6', null, '104.192.74.35', '2019-03-13 11:23:54', '2019-03-13 19:23:53', '2019-03-13 19:23:53'), ('635', '5', null, '104.192.74.35', '2019-03-13 11:23:57', '2019-03-13 19:23:57', '2019-03-13 19:23:57'), ('636', '4', null, '104.192.74.35', '2019-03-13 11:24:00', '2019-03-13 19:24:00', '2019-03-13 19:24:00'), ('637', '3', null, '104.192.74.35', '2019-03-13 11:24:10', '2019-03-13 19:24:09', '2019-03-13 19:24:09'), ('638', '4', null, '104.192.74.35', '2019-03-13 11:24:13', '2019-03-13 19:24:12', '2019-03-13 19:24:12'), ('639', '2', null, '104.192.74.35', '2019-03-13 11:24:18', '2019-03-13 19:24:17', '2019-03-13 19:24:17'), ('640', '3', null, '104.192.74.35', '2019-03-13 11:24:26', '2019-03-13 19:24:25', '2019-03-13 19:24:25'), ('641', '2', null, '104.192.74.35', '2019-03-13 11:24:37', '2019-03-13 19:24:37', '2019-03-13 19:24:37'), ('642', '9', null, '104.192.74.15', '2019-03-14 06:00:41', '2019-03-14 14:00:41', '2019-03-14 14:00:41'), ('643', '8', null, '104.192.74.15', '2019-03-14 06:00:56', '2019-03-14 14:00:55', '2019-03-14 14:00:55'), ('644', '9', null, '104.192.74.15', '2019-03-14 06:00:57', '2019-03-14 14:00:57', '2019-03-14 14:00:57'), ('645', '7', null, '104.192.74.15', '2019-03-14 06:00:57', '2019-03-14 14:00:58', '2019-03-14 14:00:58'), ('646', '6', null, '104.192.74.15', '2019-03-14 06:01:15', '2019-03-14 14:01:14', '2019-03-14 14:01:14'), ('647', '7', null, '104.192.74.15', '2019-03-14 06:01:15', '2019-03-14 14:01:14', '2019-03-14 14:01:14'), ('648', '8', null, '104.192.74.15', '2019-03-14 06:01:17', '2019-03-14 14:01:17', '2019-03-14 14:01:17'), ('649', '5', null, '104.192.74.15', '2019-03-14 06:01:26', '2019-03-14 14:01:26', '2019-03-14 14:01:26'), ('650', '6', null, '104.192.74.15', '2019-03-14 06:01:35', '2019-03-14 14:01:34', '2019-03-14 14:01:34'), ('651', '4', null, '104.192.74.15', '2019-03-14 06:01:41', '2019-03-14 14:01:41', '2019-03-14 14:01:41'), ('652', '5', null, '104.192.74.15', '2019-03-14 06:01:45', '2019-03-14 14:01:46', '2019-03-14 14:01:46'), ('653', '3', null, '104.192.74.15', '2019-03-14 06:01:49', '2019-03-14 14:01:49', '2019-03-14 14:01:49'), ('654', '4', null, '104.192.74.15', '2019-03-14 06:01:51', '2019-03-14 14:01:50', '2019-03-14 14:01:50'), ('655', '3', null, '104.192.74.15', '2019-03-14 06:02:11', '2019-03-14 14:02:11', '2019-03-14 14:02:11'), ('656', '2', null, '104.192.74.15', '2019-03-14 06:02:12', '2019-03-14 14:02:12', '2019-03-14 14:02:12'), ('657', '2', null, '104.192.74.15', '2019-03-14 06:02:42', '2019-03-14 14:02:42', '2019-03-14 14:02:42'), ('658', '4', null, '115.192.84.38', '2019-03-18 01:52:21', '2019-03-18 09:52:21', '2019-03-18 09:52:21'), ('659', '4', null, '61.129.6.151', '2019-03-18 01:53:23', '2019-03-18 09:53:40', '2019-03-18 09:53:40'), ('660', '9', null, '104.192.74.26', '2019-05-06 11:09:54', '2019-05-06 19:09:57', '2019-05-06 19:09:57'), ('661', '7', null, '104.192.74.26', '2019-05-06 11:09:56', '2019-05-06 19:09:57', '2019-05-06 19:09:57'), ('662', '8', null, '104.192.74.26', '2019-05-06 11:09:55', '2019-05-06 19:09:57', '2019-05-06 19:09:57'), ('663', '7', null, '104.192.74.26', '2019-05-06 11:10:07', '2019-05-06 19:10:07', '2019-05-06 19:10:07'), ('664', '9', null, '104.192.74.26', '2019-05-06 11:10:14', '2019-05-06 19:10:13', '2019-05-06 19:10:13'), ('665', '8', null, '104.192.74.26', '2019-05-06 11:10:16', '2019-05-06 19:10:16', '2019-05-06 19:10:16'), ('666', '5', null, '104.192.74.26', '2019-05-06 11:10:23', '2019-05-06 19:10:22', '2019-05-06 19:10:22'), ('667', '6', null, '104.192.74.26', '2019-05-06 11:10:23', '2019-05-06 19:10:22', '2019-05-06 19:10:22'), ('668', '6', null, '104.192.74.26', '2019-05-06 11:10:36', '2019-05-06 19:10:35', '2019-05-06 19:10:35'), ('669', '4', null, '104.192.74.26', '2019-05-06 11:10:36', '2019-05-06 19:10:35', '2019-05-06 19:10:35'), ('670', '5', null, '104.192.74.26', '2019-05-06 11:10:38', '2019-05-06 19:10:37', '2019-05-06 19:10:37'), ('671', '4', null, '104.192.74.26', '2019-05-06 11:10:45', '2019-05-06 19:10:44', '2019-05-06 19:10:44'), ('672', '3', null, '104.192.74.26', '2019-05-06 11:10:54', '2019-05-06 19:10:54', '2019-05-06 19:10:54'), ('673', '2', null, '104.192.74.26', '2019-05-06 11:10:54', '2019-05-06 19:10:54', '2019-05-06 19:10:54'), ('674', '2', null, '104.192.74.26', '2019-05-06 11:11:13', '2019-05-06 19:11:12', '2019-05-06 19:11:12'), ('675', '3', null, '104.192.74.26', '2019-05-06 11:11:16', '2019-05-06 19:11:16', '2019-05-06 19:11:16'), ('676', '9', null, '36.24.128.185', '2019-05-07 02:52:45', '2019-05-07 10:52:45', '2019-05-07 10:52:45'), ('677', '7', null, '36.24.128.185', '2019-05-07 02:52:53', '2019-05-07 10:52:52', '2019-05-07 10:52:52'), ('678', '8', null, '124.160.219.28', '2019-05-07 02:54:13', '2019-05-07 10:54:12', '2019-05-07 10:54:12'), ('679', '8', null, '223.166.151.197', '2019-05-07 02:55:12', '2019-05-07 10:55:12', '2019-05-07 10:55:12'), ('680', '9', null, '104.192.74.43', '2019-05-07 06:14:34', '2019-05-07 14:14:34', '2019-05-07 14:14:34'), ('681', '8', null, '104.192.74.43', '2019-05-07 06:14:40', '2019-05-07 14:14:40', '2019-05-07 14:14:40'), ('682', '7', null, '104.192.74.43', '2019-05-07 06:14:43', '2019-05-07 14:14:42', '2019-05-07 14:14:42'), ('683', '9', null, '104.192.74.43', '2019-05-07 06:14:43', '2019-05-07 14:14:43', '2019-05-07 14:14:43'), ('684', '7', null, '104.192.74.43', '2019-05-07 06:14:54', '2019-05-07 14:14:54', '2019-05-07 14:14:54'), ('685', '6', null, '104.192.74.43', '2019-05-07 06:14:55', '2019-05-07 14:14:54', '2019-05-07 14:14:54'), ('686', '8', null, '104.192.74.43', '2019-05-07 06:14:56', '2019-05-07 14:14:55', '2019-05-07 14:14:55'), ('687', '5', null, '104.192.74.43', '2019-05-07 06:15:07', '2019-05-07 14:15:07', '2019-05-07 14:15:07'), ('688', '6', null, '104.192.74.43', '2019-05-07 06:15:13', '2019-05-07 14:15:12', '2019-05-07 14:15:12'), ('689', '4', null, '104.192.74.43', '2019-05-07 06:15:15', '2019-05-07 14:15:14', '2019-05-07 14:15:14'), ('690', '4', null, '104.192.74.43', '2019-05-07 06:15:22', '2019-05-07 14:15:21', '2019-05-07 14:15:21'), ('691', '3', null, '104.192.74.43', '2019-05-07 06:15:28', '2019-05-07 14:15:27', '2019-05-07 14:15:27'), ('692', '2', null, '104.192.74.43', '2019-05-07 06:15:28', '2019-05-07 14:15:28', '2019-05-07 14:15:28'), ('693', '5', null, '104.192.74.43', '2019-05-07 06:15:28', '2019-05-07 14:15:28', '2019-05-07 14:15:28'), ('694', '2', null, '104.192.74.43', '2019-05-07 06:15:46', '2019-05-07 14:15:45', '2019-05-07 14:15:45'), ('695', '3', null, '104.192.74.43', '2019-05-07 06:15:49', '2019-05-07 14:15:49', '2019-05-07 14:15:49'), ('696', '3', null, '58.251.121.184', '2019-05-07 11:17:16', '2019-05-07 19:17:16', '2019-05-07 19:17:16'), ('697', '6', null, '58.251.121.184', '2019-05-07 12:06:34', '2019-05-07 20:06:33', '2019-05-07 20:06:33'), ('698', '9', null, '104.192.74.43', '2019-05-08 18:39:21', '2019-05-09 02:39:20', '2019-05-09 02:39:20'), ('699', '8', null, '104.192.74.43', '2019-05-08 18:39:22', '2019-05-09 02:39:21', '2019-05-09 02:39:21'), ('700', '7', null, '104.192.74.43', '2019-05-08 18:39:30', '2019-05-09 02:39:29', '2019-05-09 02:39:29'), ('701', '9', null, '104.192.74.43', '2019-05-08 18:39:34', '2019-05-09 02:39:33', '2019-05-09 02:39:33'), ('702', '7', null, '104.192.74.43', '2019-05-08 18:39:37', '2019-05-09 02:39:37', '2019-05-09 02:39:37'), ('703', '6', null, '104.192.74.43', '2019-05-08 18:39:39', '2019-05-09 02:39:39', '2019-05-09 02:39:39'), ('704', '8', null, '104.192.74.43', '2019-05-08 18:39:42', '2019-05-09 02:39:41', '2019-05-09 02:39:41'), ('705', '5', null, '104.192.74.43', '2019-05-08 18:39:48', '2019-05-09 02:39:47', '2019-05-09 02:39:47'), ('706', '6', null, '104.192.74.43', '2019-05-08 18:39:59', '2019-05-09 02:39:59', '2019-05-09 02:39:59'), ('707', '4', null, '104.192.74.43', '2019-05-08 18:40:02', '2019-05-09 02:40:01', '2019-05-09 02:40:01'), ('708', '5', null, '104.192.74.43', '2019-05-08 18:40:05', '2019-05-09 02:40:05', '2019-05-09 02:40:05'), ('709', '4', null, '104.192.74.43', '2019-05-08 18:40:10', '2019-05-09 02:40:10', '2019-05-09 02:40:10'), ('710', '3', null, '104.192.74.43', '2019-05-08 18:40:14', '2019-05-09 02:40:13', '2019-05-09 02:40:13'), ('711', '2', null, '104.192.74.43', '2019-05-08 18:40:25', '2019-05-09 02:40:24', '2019-05-09 02:40:24'), ('712', '3', null, '104.192.74.43', '2019-05-08 18:40:27', '2019-05-09 02:40:26', '2019-05-09 02:40:26'), ('713', '2', null, '104.192.74.43', '2019-05-08 18:40:43', '2019-05-09 02:40:43', '2019-05-09 02:40:43'), ('714', '9', null, '104.192.74.35', '2019-05-09 11:39:58', '2019-05-09 19:39:58', '2019-05-09 19:39:58'), ('715', '8', null, '104.192.74.35', '2019-05-09 11:39:58', '2019-05-09 19:39:58', '2019-05-09 19:39:58'), ('716', '7', null, '104.192.74.35', '2019-05-09 11:39:58', '2019-05-09 19:39:58', '2019-05-09 19:39:58'), ('717', '9', null, '104.192.74.35', '2019-05-09 11:40:06', '2019-05-09 19:40:06', '2019-05-09 19:40:06'), ('718', '7', null, '104.192.74.35', '2019-05-09 11:40:10', '2019-05-09 19:40:09', '2019-05-09 19:40:09'), ('719', '8', null, '104.192.74.35', '2019-05-09 11:40:19', '2019-05-09 19:40:18', '2019-05-09 19:40:18'), ('720', '5', null, '104.192.74.35', '2019-05-09 11:40:21', '2019-05-09 19:40:21', '2019-05-09 19:40:21'), ('721', '6', null, '104.192.74.35', '2019-05-09 11:40:21', '2019-05-09 19:40:21', '2019-05-09 19:40:21'), ('722', '6', null, '104.192.74.35', '2019-05-09 11:40:36', '2019-05-09 19:40:36', '2019-05-09 19:40:36'), ('723', '5', null, '104.192.74.35', '2019-05-09 11:40:40', '2019-05-09 19:40:40', '2019-05-09 19:40:40'), ('724', '4', null, '104.192.74.35', '2019-05-09 11:40:49', '2019-05-09 19:40:49', '2019-05-09 19:40:49'), ('725', '3', null, '104.192.74.35', '2019-05-09 11:40:50', '2019-05-09 19:40:49', '2019-05-09 19:40:49'), ('726', '2', null, '104.192.74.35', '2019-05-09 11:40:50', '2019-05-09 19:40:50', '2019-05-09 19:40:50'), ('727', '4', null, '104.192.74.35', '2019-05-09 11:40:58', '2019-05-09 19:40:58', '2019-05-09 19:40:58'), ('728', '3', null, '104.192.74.35', '2019-05-09 11:41:09', '2019-05-09 19:41:08', '2019-05-09 19:41:08'), ('729', '2', null, '104.192.74.35', '2019-05-09 11:41:09', '2019-05-09 19:41:08', '2019-05-09 19:41:08'), ('730', '9', null, '104.192.74.15', '2019-05-10 07:10:19', '2019-05-10 15:10:19', '2019-05-10 15:10:19'), ('731', '8', null, '104.192.74.15', '2019-05-10 07:10:20', '2019-05-10 15:10:19', '2019-05-10 15:10:19'), ('732', '7', null, '104.192.74.15', '2019-05-10 07:10:21', '2019-05-10 15:10:20', '2019-05-10 15:10:20'), ('733', '7', null, '104.192.74.15', '2019-05-10 07:10:31', '2019-05-10 15:10:31', '2019-05-10 15:10:31'), ('734', '6', null, '104.192.74.15', '2019-05-10 07:10:35', '2019-05-10 15:10:35', '2019-05-10 15:10:35'), ('735', '9', null, '104.192.74.15', '2019-05-10 07:10:36', '2019-05-10 15:10:36', '2019-05-10 15:10:36'), ('736', '8', null, '104.192.74.15', '2019-05-10 07:10:41', '2019-05-10 15:10:40', '2019-05-10 15:10:40'), ('737', '6', null, '104.192.74.15', '2019-05-10 07:10:47', '2019-05-10 15:10:46', '2019-05-10 15:10:46'), ('738', '5', null, '104.192.74.15', '2019-05-10 07:10:55', '2019-05-10 15:10:55', '2019-05-10 15:10:55'), ('739', '4', null, '104.192.74.15', '2019-05-10 07:11:03', '2019-05-10 15:11:03', '2019-05-10 15:11:03'), ('740', '3', null, '104.192.74.15', '2019-05-10 07:11:03', '2019-05-10 15:11:03', '2019-05-10 15:11:03'), ('741', '5', null, '104.192.74.15', '2019-05-10 07:11:05', '2019-05-10 15:11:05', '2019-05-10 15:11:05'), ('742', '4', null, '104.192.74.15', '2019-05-10 07:11:13', '2019-05-10 15:11:12', '2019-05-10 15:11:12'), ('743', '2', null, '104.192.74.15', '2019-05-10 07:11:21', '2019-05-10 15:11:21', '2019-05-10 15:11:21'), ('744', '3', null, '104.192.74.15', '2019-05-10 07:11:24', '2019-05-10 15:11:24', '2019-05-10 15:11:24'), ('745', '2', null, '104.192.74.15', '2019-05-10 07:11:39', '2019-05-10 15:11:38', '2019-05-10 15:11:38'), ('746', '9', null, '104.192.74.23', '2019-05-11 19:07:52', '2019-05-12 03:07:51', '2019-05-12 03:07:51'), ('747', '8', null, '104.192.74.23', '2019-05-11 19:07:57', '2019-05-12 03:07:57', '2019-05-12 03:07:57'), ('748', '7', null, '104.192.74.23', '2019-05-11 19:08:02', '2019-05-12 03:08:01', '2019-05-12 03:08:01'), ('749', '9', null, '104.192.74.23', '2019-05-11 19:08:04', '2019-05-12 03:08:03', '2019-05-12 03:08:03'), ('750', '6', null, '104.192.74.23', '2019-05-11 19:08:11', '2019-05-12 03:08:10', '2019-05-12 03:08:10'), ('751', '7', null, '104.192.74.23', '2019-05-11 19:08:12', '2019-05-12 03:08:11', '2019-05-12 03:08:11'), ('752', '8', null, '104.192.74.23', '2019-05-11 19:08:15', '2019-05-12 03:08:15', '2019-05-12 03:08:15'), ('753', '6', null, '104.192.74.23', '2019-05-11 19:08:23', '2019-05-12 03:08:22', '2019-05-12 03:08:22'), ('754', '5', null, '104.192.74.23', '2019-05-11 19:08:25', '2019-05-12 03:08:25', '2019-05-12 03:08:25'), ('755', '4', null, '104.192.74.23', '2019-05-11 19:08:34', '2019-05-12 03:08:34', '2019-05-12 03:08:34'), ('756', '3', null, '104.192.74.23', '2019-05-11 19:08:41', '2019-05-12 03:08:40', '2019-05-12 03:08:40'), ('757', '4', null, '104.192.74.23', '2019-05-11 19:08:41', '2019-05-12 03:08:41', '2019-05-12 03:08:41'), ('758', '5', null, '104.192.74.23', '2019-05-11 19:08:47', '2019-05-12 03:08:46', '2019-05-12 03:08:46'), ('759', '3', null, '104.192.74.23', '2019-05-11 19:08:57', '2019-05-12 03:08:57', '2019-05-12 03:08:57'), ('760', '2', null, '104.192.74.23', '2019-05-11 19:08:57', '2019-05-12 03:08:57', '2019-05-12 03:08:57'), ('761', '2', null, '104.192.74.23', '2019-05-11 19:09:19', '2019-05-12 03:09:18', '2019-05-12 03:09:18'), ('762', '7', null, '101.226.79.190', '2019-05-11 23:58:40', '2019-05-12 07:58:40', '2019-05-12 07:58:40'), ('763', '5', null, '101.91.62.11', '2019-05-12 00:29:00', '2019-05-12 08:28:59', '2019-05-12 08:28:59'), ('764', '9', null, '59.36.119.226', '2019-05-12 00:49:58', '2019-05-12 08:49:57', '2019-05-12 08:49:57'), ('765', '8', null, '104.192.74.35', '2019-05-12 16:16:41', '2019-05-13 00:16:41', '2019-05-13 00:16:41'), ('766', '9', null, '104.192.74.35', '2019-05-12 16:16:41', '2019-05-13 00:16:41', '2019-05-13 00:16:41'), ('767', '8', null, '104.192.74.35', '2019-05-12 16:16:55', '2019-05-13 00:16:54', '2019-05-13 00:16:54'), ('768', '7', null, '104.192.74.35', '2019-05-12 16:17:02', '2019-05-13 00:17:01', '2019-05-13 00:17:01'), ('769', '7', null, '104.192.74.35', '2019-05-12 16:17:05', '2019-05-13 00:17:05', '2019-05-13 00:17:05'), ('770', '6', null, '104.192.74.35', '2019-05-12 16:17:06', '2019-05-13 00:17:05', '2019-05-13 00:17:05'), ('771', '9', null, '104.192.74.35', '2019-05-12 16:17:08', '2019-05-13 00:17:07', '2019-05-13 00:17:07'), ('772', '6', null, '104.192.74.35', '2019-05-12 16:17:16', '2019-05-13 00:17:16', '2019-05-13 00:17:16'), ('773', '5', null, '104.192.74.35', '2019-05-12 16:17:19', '2019-05-13 00:17:18', '2019-05-13 00:17:18'), ('774', '4', null, '104.192.74.35', '2019-05-12 16:17:19', '2019-05-13 00:17:18', '2019-05-13 00:17:18'), ('775', '4', null, '104.192.74.35', '2019-05-12 16:17:32', '2019-05-13 00:17:31', '2019-05-13 00:17:31'), ('776', '5', null, '104.192.74.35', '2019-05-12 16:17:33', '2019-05-13 00:17:32', '2019-05-13 00:17:32'), ('777', '3', null, '104.192.74.35', '2019-05-12 16:17:39', '2019-05-13 00:17:39', '2019-05-13 00:17:39'), ('778', '2', null, '104.192.74.35', '2019-05-12 16:17:45', '2019-05-13 00:17:45', '2019-05-13 00:17:45'), ('779', '3', null, '104.192.74.35', '2019-05-12 16:17:55', '2019-05-13 00:17:55', '2019-05-13 00:17:55'), ('780', '2', null, '104.192.74.35', '2019-05-12 16:18:01', '2019-05-13 00:18:01', '2019-05-13 00:18:01'), ('781', '4', null, '14.17.3.64', '2019-05-12 20:33:25', '2019-05-13 04:33:25', '2019-05-13 04:33:25'), ('782', '3', null, '59.36.119.227', '2019-05-12 20:52:35', '2019-05-13 04:52:35', '2019-05-13 04:52:35'), ('783', '7', null, '59.36.119.227', '2019-05-12 21:13:40', '2019-05-13 05:13:39', '2019-05-13 05:13:39'), ('784', '9', null, '104.192.74.17', '2019-05-13 07:09:31', '2019-05-13 15:09:30', '2019-05-13 15:09:30'), ('785', '7', null, '104.192.74.17', '2019-05-13 07:09:31', '2019-05-13 15:09:30', '2019-05-13 15:09:30'), ('786', '8', null, '104.192.74.17', '2019-05-13 07:09:31', '2019-05-13 15:09:30', '2019-05-13 15:09:30'), ('787', '9', null, '104.192.74.17', '2019-05-13 07:09:41', '2019-05-13 15:09:41', '2019-05-13 15:09:41'), ('788', '7', null, '104.192.74.17', '2019-05-13 07:09:45', '2019-05-13 15:09:45', '2019-05-13 15:09:45'), ('789', '8', null, '104.192.74.17', '2019-05-13 07:09:45', '2019-05-13 15:09:45', '2019-05-13 15:09:45'), ('790', '6', null, '104.192.74.17', '2019-05-13 07:09:51', '2019-05-13 15:09:51', '2019-05-13 15:09:51'), ('791', '5', null, '104.192.74.17', '2019-05-13 07:09:56', '2019-05-13 15:09:55', '2019-05-13 15:09:55'), ('792', '4', null, '104.192.74.17', '2019-05-13 07:10:07', '2019-05-13 15:10:06', '2019-05-13 15:10:06'), ('793', '6', null, '104.192.74.17', '2019-05-13 07:10:07', '2019-05-13 15:10:07', '2019-05-13 15:10:07'), ('794', '4', null, '104.192.74.17', '2019-05-13 07:10:16', '2019-05-13 15:10:15', '2019-05-13 15:10:15'), ('795', '5', null, '104.192.74.17', '2019-05-13 07:10:16', '2019-05-13 15:10:16', '2019-05-13 15:10:16'), ('796', '3', null, '104.192.74.17', '2019-05-13 07:10:25', '2019-05-13 15:10:25', '2019-05-13 15:10:25'), ('797', '2', null, '104.192.74.17', '2019-05-13 07:10:27', '2019-05-13 15:10:27', '2019-05-13 15:10:27'), ('798', '2', null, '104.192.74.17', '2019-05-13 07:10:44', '2019-05-13 15:10:43', '2019-05-13 15:10:43'), ('799', '3', null, '104.192.74.17', '2019-05-13 07:10:46', '2019-05-13 15:10:46', '2019-05-13 15:10:46'), ('800', '5', null, '58.251.121.186', '2019-05-13 15:52:11', '2019-05-13 23:52:10', '2019-05-13 23:52:10'), ('801', '6', null, '14.17.21.58', '2019-05-13 16:21:44', '2019-05-14 00:21:44', '2019-05-14 00:21:44'), ('802', '2', null, '59.36.119.226', '2019-05-13 16:51:04', '2019-05-14 00:51:04', '2019-05-14 00:51:04'), ('803', '8', null, '14.17.3.65', '2019-05-13 18:13:02', '2019-05-14 02:13:02', '2019-05-14 02:13:02'), ('804', '9', null, '115.200.243.140', '2019-05-14 02:38:44', '2019-05-14 10:38:44', '2019-05-14 10:38:44'), ('805', '9', null, '115.200.243.140', '2019-05-14 02:38:46', '2019-05-14 10:38:45', '2019-05-14 10:38:45'), ('806', '9', null, '104.192.74.17', '2019-05-14 12:58:15', '2019-05-14 20:58:15', '2019-05-14 20:58:15'), ('807', '7', null, '104.192.74.17', '2019-05-14 12:58:33', '2019-05-14 20:58:32', '2019-05-14 20:58:32'), ('808', '8', null, '104.192.74.17', '2019-05-14 12:58:33', '2019-05-14 20:58:33', '2019-05-14 20:58:33'), ('809', '9', null, '104.192.74.17', '2019-05-14 12:58:34', '2019-05-14 20:58:34', '2019-05-14 20:58:34'), ('810', '6', null, '104.192.74.17', '2019-05-14 12:58:39', '2019-05-14 20:58:38', '2019-05-14 20:58:38'), ('811', '7', null, '104.192.74.17', '2019-05-14 12:58:47', '2019-05-14 20:58:46', '2019-05-14 20:58:46'), ('812', '5', null, '104.192.74.17', '2019-05-14 12:58:54', '2019-05-14 20:58:53', '2019-05-14 20:58:53'), ('813', '8', null, '104.192.74.17', '2019-05-14 12:58:54', '2019-05-14 20:58:53', '2019-05-14 20:58:53'), ('814', '6', null, '104.192.74.17', '2019-05-14 12:58:54', '2019-05-14 20:58:54', '2019-05-14 20:58:54'), ('815', '5', null, '104.192.74.17', '2019-05-14 12:59:13', '2019-05-14 20:59:12', '2019-05-14 20:59:12'), ('816', '2', null, '104.192.74.17', '2019-05-14 12:59:20', '2019-05-14 20:59:19', '2019-05-14 20:59:19'), ('817', '4', null, '104.192.74.17', '2019-05-14 12:59:24', '2019-05-14 20:59:24', '2019-05-14 20:59:24'), ('818', '3', null, '104.192.74.17', '2019-05-14 12:59:24', '2019-05-14 20:59:24', '2019-05-14 20:59:24'), ('819', '2', null, '104.192.74.17', '2019-05-14 12:59:28', '2019-05-14 20:59:28', '2019-05-14 20:59:28'), ('820', '4', null, '104.192.74.17', '2019-05-14 12:59:32', '2019-05-14 20:59:32', '2019-05-14 20:59:32'), ('821', '3', null, '104.192.74.17', '2019-05-14 12:59:41', '2019-05-14 20:59:40', '2019-05-14 20:59:40'), ('822', '9', null, '60.176.191.113', '2019-05-15 01:23:05', '2019-05-15 09:23:04', '2019-05-15 09:23:04'), ('823', '8', null, '60.176.191.113', '2019-05-15 01:23:20', '2019-05-15 09:23:19', '2019-05-15 09:23:19'), ('824', '9', null, '104.192.74.40', '2019-05-15 08:56:34', '2019-05-15 16:56:33', '2019-05-15 16:56:33'), ('825', '7', null, '104.192.74.40', '2019-05-15 08:56:34', '2019-05-15 16:56:34', '2019-05-15 16:56:34'), ('826', '8', null, '104.192.74.40', '2019-05-15 08:56:35', '2019-05-15 16:56:34', '2019-05-15 16:56:34'), ('827', '7', null, '104.192.74.40', '2019-05-15 08:56:46', '2019-05-15 16:56:45', '2019-05-15 16:56:45'), ('828', '8', null, '104.192.74.40', '2019-05-15 08:56:49', '2019-05-15 16:56:48', '2019-05-15 16:56:48'), ('829', '9', null, '104.192.74.40', '2019-05-15 08:56:51', '2019-05-15 16:56:51', '2019-05-15 16:56:51'), ('830', '5', null, '104.192.74.40', '2019-05-15 08:57:03', '2019-05-15 16:57:02', '2019-05-15 16:57:02'), ('831', '6', null, '104.192.74.40', '2019-05-15 08:57:03', '2019-05-15 16:57:02', '2019-05-15 16:57:02'), ('832', '4', null, '104.192.74.40', '2019-05-15 08:57:10', '2019-05-15 16:57:10', '2019-05-15 16:57:10'), ('833', '4', null, '104.192.74.40', '2019-05-15 08:57:18', '2019-05-15 16:57:17', '2019-05-15 16:57:17'), ('834', '6', null, '104.192.74.40', '2019-05-15 08:57:18', '2019-05-15 16:57:17', '2019-05-15 16:57:17'), ('835', '5', null, '104.192.74.40', '2019-05-15 08:57:18', '2019-05-15 16:57:17', '2019-05-15 16:57:17'), ('836', '3', null, '104.192.74.40', '2019-05-15 08:57:39', '2019-05-15 16:57:39', '2019-05-15 16:57:39'), ('837', '2', null, '104.192.74.40', '2019-05-15 08:57:40', '2019-05-15 16:57:39', '2019-05-15 16:57:39'), ('838', '2', null, '104.192.74.40', '2019-05-15 08:58:00', '2019-05-15 16:58:00', '2019-05-15 16:58:00'), ('839', '3', null, '104.192.74.40', '2019-05-15 08:58:05', '2019-05-15 16:58:04', '2019-05-15 16:58:04'), ('840', '8', null, '58.251.121.185', '2019-05-15 15:19:00', '2019-05-15 23:19:00', '2019-05-15 23:19:00'), ('841', '7', null, '163.177.90.152', '2019-05-15 15:22:16', '2019-05-15 23:22:15', '2019-05-15 23:22:15'), ('842', '3', null, '101.226.114.193', '2019-05-15 15:38:37', '2019-05-15 23:38:36', '2019-05-15 23:38:36'), ('843', '9', null, '14.17.3.64', '2019-05-15 18:48:24', '2019-05-16 02:48:24', '2019-05-16 02:48:24'), ('844', '6', null, '14.17.3.64', '2019-05-15 19:25:38', '2019-05-16 03:25:37', '2019-05-16 03:25:37'), ('845', '5', null, '59.36.119.227', '2019-05-15 20:58:48', '2019-05-16 04:58:47', '2019-05-16 04:58:47'), ('846', '5', null, '101.226.79.190', '2019-05-16 03:11:13', '2019-05-16 11:11:12', '2019-05-16 11:11:12'), ('847', '9', null, '104.192.74.13', '2019-05-16 15:46:06', '2019-05-16 23:46:05', '2019-05-16 23:46:05'), ('848', '9', null, '104.192.74.13', '2019-05-16 15:46:16', '2019-05-16 23:46:15', '2019-05-16 23:46:15'), ('849', '8', null, '104.192.74.13', '2019-05-16 15:46:16', '2019-05-16 23:46:16', '2019-05-16 23:46:16'), ('850', '7', null, '104.192.74.13', '2019-05-16 15:46:21', '2019-05-16 23:46:20', '2019-05-16 23:46:20'), ('851', '7', null, '104.192.74.13', '2019-05-16 15:46:29', '2019-05-16 23:46:29', '2019-05-16 23:46:29'), ('852', '8', null, '104.192.74.13', '2019-05-16 15:46:30', '2019-05-16 23:46:29', '2019-05-16 23:46:29'), ('853', '6', null, '104.192.74.13', '2019-05-16 15:46:31', '2019-05-16 23:46:30', '2019-05-16 23:46:30'), ('854', '6', null, '104.192.74.13', '2019-05-16 15:46:45', '2019-05-16 23:46:44', '2019-05-16 23:46:44'), ('855', '5', null, '104.192.74.13', '2019-05-16 15:46:45', '2019-05-16 23:46:45', '2019-05-16 23:46:45'), ('856', '4', null, '104.192.74.13', '2019-05-16 15:46:46', '2019-05-16 23:46:45', '2019-05-16 23:46:45'), ('857', '5', null, '104.192.74.13', '2019-05-16 15:47:00', '2019-05-16 23:47:00', '2019-05-16 23:47:00'), ('858', '3', null, '104.192.74.13', '2019-05-16 15:47:05', '2019-05-16 23:47:05', '2019-05-16 23:47:05'), ('859', '4', null, '104.192.74.13', '2019-05-16 15:47:08', '2019-05-16 23:47:07', '2019-05-16 23:47:07'), ('860', '3', null, '104.192.74.13', '2019-05-16 15:47:16', '2019-05-16 23:47:16', '2019-05-16 23:47:16'), ('861', '2', null, '104.192.74.13', '2019-05-16 15:47:18', '2019-05-16 23:47:18', '2019-05-16 23:47:18'), ('862', '2', null, '104.192.74.13', '2019-05-16 15:47:34', '2019-05-16 23:47:34', '2019-05-16 23:47:34'), ('863', '7', null, '46.101.9.216', '2019-05-31 14:01:13', '2019-05-31 22:01:12', '2019-05-31 22:01:12'), ('864', '6', null, '46.101.9.216', '2019-05-31 14:01:16', '2019-05-31 22:01:16', '2019-05-31 22:01:16'), ('865', '5', null, '46.101.9.216', '2019-05-31 14:01:24', '2019-05-31 22:01:24', '2019-05-31 22:01:24'), ('866', '4', null, '42.156.137.60', '2019-06-04 07:59:14', '2019-06-04 15:59:14', '2019-06-04 15:59:14'), ('867', '8', null, '106.11.153.126', '2019-06-08 04:32:12', '2019-06-08 12:32:11', '2019-06-08 12:32:11'), ('868', '6', null, '222.137.47.153', '2019-06-08 08:07:32', '2019-06-08 16:07:31', '2019-06-08 16:07:31'), ('869', '5', null, '222.137.47.153', '2019-06-08 08:07:33', '2019-06-08 16:07:33', '2019-06-08 16:07:33'), ('870', '4', null, '222.137.47.153', '2019-06-08 08:07:34', '2019-06-08 16:07:33', '2019-06-08 16:07:33'), ('871', '9', null, '222.137.47.153', '2019-06-08 08:07:34', '2019-06-08 16:07:34', '2019-06-08 16:07:34'), ('872', '2', null, '222.137.47.153', '2019-06-08 08:07:36', '2019-06-08 16:07:35', '2019-06-08 16:07:35'), ('873', '3', null, '222.137.47.153', '2019-06-08 08:07:37', '2019-06-08 16:07:37', '2019-06-08 16:07:37'), ('874', '7', null, '222.137.47.153', '2019-06-08 08:07:39', '2019-06-08 16:07:38', '2019-06-08 16:07:38'), ('875', '8', null, '222.137.47.153', '2019-06-08 08:07:39', '2019-06-08 16:07:39', '2019-06-08 16:07:39'), ('876', '2', null, '42.156.137.119', '2019-06-08 10:16:34', '2019-06-08 18:16:34', '2019-06-08 18:16:34'), ('877', '7', null, '42.156.254.56', '2019-06-09 02:32:48', '2019-06-09 10:32:48', '2019-06-09 10:32:48'), ('878', '3', null, '42.156.254.112', '2019-06-09 07:09:30', '2019-06-09 15:09:29', '2019-06-09 15:09:29'), ('879', '5', null, '42.120.161.20', '2019-06-09 11:37:25', '2019-06-09 19:37:25', '2019-06-09 19:37:25'), ('880', '9', null, '106.11.158.75', '2019-06-11 02:09:00', '2019-06-11 10:09:00', '2019-06-11 10:09:00'), ('881', '9', null, '163.177.90.152', '2019-06-11 08:03:13', '2019-06-11 16:03:13', '2019-06-11 16:03:13'), ('882', '6', null, '42.120.160.110', '2019-06-11 16:33:55', '2019-06-12 00:33:55', '2019-06-12 00:33:55'), ('883', '5', null, '106.11.159.43', '2019-06-11 21:25:11', '2019-06-12 05:25:10', '2019-06-12 05:25:10'), ('884', '9', null, '104.192.74.12', '2019-06-17 07:12:02', '2019-06-17 15:12:01', '2019-06-17 15:12:01'), ('885', '8', null, '104.192.74.12', '2019-06-17 07:12:11', '2019-06-17 15:12:10', '2019-06-17 15:12:10'), ('886', '9', null, '104.192.74.12', '2019-06-17 07:12:16', '2019-06-17 15:12:16', '2019-06-17 15:12:16'), ('887', '7', null, '104.192.74.12', '2019-06-17 07:12:18', '2019-06-17 15:12:18', '2019-06-17 15:12:18'), ('888', '8', null, '104.192.74.12', '2019-06-17 07:12:27', '2019-06-17 15:12:27', '2019-06-17 15:12:27'), ('889', '7', null, '104.192.74.12', '2019-06-17 07:12:29', '2019-06-17 15:12:28', '2019-06-17 15:12:28'), ('890', '6', null, '104.192.74.12', '2019-06-17 07:12:30', '2019-06-17 15:12:29', '2019-06-17 15:12:29'), ('891', '5', null, '104.192.74.12', '2019-06-17 07:12:40', '2019-06-17 15:12:39', '2019-06-17 15:12:39'), ('892', '6', null, '104.192.74.12', '2019-06-17 07:12:47', '2019-06-17 15:12:46', '2019-06-17 15:12:46'), ('893', '4', null, '104.192.74.12', '2019-06-17 07:12:52', '2019-06-17 15:12:52', '2019-06-17 15:12:52'), ('894', '5', null, '104.192.74.12', '2019-06-17 07:13:01', '2019-06-17 15:13:01', '2019-06-17 15:13:01'), ('895', '3', null, '104.192.74.12', '2019-06-17 07:13:02', '2019-06-17 15:13:01', '2019-06-17 15:13:01'), ('896', '4', null, '104.192.74.12', '2019-06-17 07:13:02', '2019-06-17 15:13:01', '2019-06-17 15:13:01'), ('897', '2', null, '104.192.74.12', '2019-06-17 07:13:18', '2019-06-17 15:13:18', '2019-06-17 15:13:18'), ('898', '3', null, '104.192.74.12', '2019-06-17 07:13:18', '2019-06-17 15:13:18', '2019-06-17 15:13:18'), ('899', '2', null, '104.192.74.12', '2019-06-17 07:13:36', '2019-06-17 15:13:36', '2019-06-17 15:13:36'), ('900', '4', null, '42.120.160.113', '2019-06-22 06:31:00', '2019-06-22 14:31:00', '2019-06-22 14:31:00'), ('901', '6', null, '42.156.137.122', '2019-06-22 17:38:32', '2019-06-23 01:38:32', '2019-06-23 01:38:32'), ('902', '6', null, '101.226.114.193', '2019-06-22 22:42:37', '2019-06-23 06:42:37', '2019-06-23 06:42:37'), ('903', '2', null, '106.11.157.154', '2019-06-27 13:14:01', '2019-06-27 21:14:01', '2019-06-27 21:14:01'), ('904', '7', null, '42.120.160.52', '2019-06-27 18:07:29', '2019-06-28 02:07:28', '2019-06-28 02:07:28'), ('905', '9', null, '106.11.158.37', '2019-06-30 09:14:40', '2019-06-30 17:14:41', '2019-06-30 17:14:41');
COMMIT;

-- ----------------------------
--  Table structure for `biz_article_love`
-- ----------------------------
DROP TABLE IF EXISTS `biz_article_love`;
CREATE TABLE `biz_article_love` (
  `id` bigint(11) unsigned NOT NULL AUTO_INCREMENT,
  `article_id` bigint(11) unsigned NOT NULL COMMENT '文章ID',
  `user_id` bigint(11) unsigned DEFAULT NULL COMMENT '已登录用户ID',
  `user_ip` varchar(50) DEFAULT NULL COMMENT '用户IP',
  `love_time` datetime DEFAULT NULL COMMENT '浏览时间',
  `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '添加时间',
  `update_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT COMMENT='文章喜欢';

-- ----------------------------
--  Records of `biz_article_love`
-- ----------------------------
BEGIN;
INSERT INTO `biz_article_love` VALUES ('1', '2', null, '218.74.30.199', '2019-01-30 06:15:35', '2019-01-30 06:15:35', '2019-01-30 06:15:35'), ('2', '3', null, '112.17.245.17', '2019-02-05 03:03:44', '2019-02-05 03:03:44', '2019-02-05 03:03:44'), ('3', '4', null, '122.233.194.154', '2019-02-13 05:48:03', '2019-02-13 05:48:03', '2019-02-13 05:48:03'), ('4', '4', null, '223.104.246.200', '2019-02-13 06:35:40', '2019-02-13 06:35:40', '2019-02-13 06:35:40'), ('5', '4', '1', '122.233.194.154', '2019-02-13 08:23:08', '2019-02-13 08:23:08', '2019-02-13 08:23:08'), ('6', '7', null, '122.233.194.154', '2019-02-14 05:53:53', '2019-02-14 05:53:53', '2019-02-14 05:53:53'), ('7', '7', null, '112.17.247.117', '2019-02-14 06:11:25', '2019-02-14 06:11:25', '2019-02-14 06:11:25');
COMMIT;

-- ----------------------------
--  Table structure for `biz_article_oppose`
-- ----------------------------
DROP TABLE IF EXISTS `biz_article_oppose`;
CREATE TABLE `biz_article_oppose` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `article_id` bigint(20) unsigned NOT NULL COMMENT '文章ID',
  `user_id` bigint(20) unsigned DEFAULT NULL COMMENT '已登录用户ID',
  `user_ip` varchar(50) DEFAULT NULL COMMENT '用户IP',
  `oppose_time` datetime DEFAULT NULL COMMENT '浏览时间',
  `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '添加时间',
  `update_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT;

-- ----------------------------
--  Records of `biz_article_oppose`
-- ----------------------------
BEGIN;
INSERT INTO `biz_article_oppose` VALUES ('1', '2', null, '218.74.30.199', '2019-01-30 06:15:44', '2019-01-30 06:15:44', '2019-01-30 06:15:44'), ('2', '7', null, '112.17.247.117', '2019-02-14 06:11:27', '2019-02-14 06:11:27', '2019-02-14 06:11:27');
COMMIT;

-- ----------------------------
--  Table structure for `biz_article_tags`
-- ----------------------------
DROP TABLE IF EXISTS `biz_article_tags`;
CREATE TABLE `biz_article_tags` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `tag_id` int(11) unsigned NOT NULL COMMENT '标签表主键',
  `article_id` int(11) unsigned NOT NULL COMMENT '文章ID',
  `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '添加时间',
  `update_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT COMMENT='文章标签';

-- ----------------------------
--  Records of `biz_article_tags`
-- ----------------------------
BEGIN;
INSERT INTO `biz_article_tags` VALUES ('1', '1', '1', '2019-01-30 03:00:38', '2019-01-30 03:00:38'), ('2', '1', '2', '2019-01-30 03:00:40', '2019-01-30 03:00:40'), ('3', '1', '3', '2019-02-03 07:14:32', '2019-02-03 07:14:32'), ('4', '2', '4', '2019-02-13 05:30:33', '2019-02-13 05:30:33'), ('5', '1', '5', '2019-02-14 03:33:54', '2019-02-14 03:33:54'), ('6', '1', '6', '2019-02-14 03:34:46', '2019-02-14 03:34:46'), ('7', '2', '7', '2019-02-14 03:37:23', '2019-02-14 03:37:23'), ('8', '1', '8', '2019-02-15 06:47:57', '2019-02-15 06:47:57'), ('9', '2', '9', '2019-02-15 06:49:34', '2019-02-15 06:49:34');
COMMIT;

-- ----------------------------
--  Table structure for `biz_comment`
-- ----------------------------
DROP TABLE IF EXISTS `biz_comment`;
CREATE TABLE `biz_comment` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `sid` int(11) DEFAULT NULL COMMENT '被评论的文章或者页面的ID',
  `user_id` int(11) unsigned DEFAULT NULL COMMENT '评论人的ID',
  `pid` int(11) unsigned DEFAULT NULL COMMENT '父级评论的id',
  `qq` varchar(13) DEFAULT NULL COMMENT '评论人的QQ（未登录用户）',
  `nickname` varchar(13) DEFAULT NULL COMMENT '评论人的昵称（未登录用户）',
  `avatar` varchar(255) DEFAULT NULL COMMENT '评论人的头像地址',
  `email` varchar(100) DEFAULT NULL COMMENT '评论人的邮箱地址（未登录用户）',
  `url` varchar(200) DEFAULT NULL COMMENT '评论人的网站地址（未登录用户）',
  `status` varchar(12) DEFAULT 'VERIFYING' COMMENT '评论的状态 VERIFYING ,  APPROVED ,  REJECT ,  DELETED ',
  `ip` varchar(64) DEFAULT NULL COMMENT '评论时的ip',
  `lng` varchar(50) DEFAULT NULL COMMENT '经度',
  `lat` varchar(50) DEFAULT NULL COMMENT '纬度',
  `address` varchar(100) DEFAULT NULL COMMENT '评论时的地址',
  `os` varchar(64) DEFAULT NULL COMMENT '评论时的系统类型',
  `os_short_name` varchar(10) DEFAULT NULL COMMENT '评论时的系统的简称',
  `browser` varchar(64) DEFAULT NULL COMMENT '评论时的浏览器类型',
  `browser_short_name` varchar(10) DEFAULT NULL COMMENT '评论时的浏览器的简称',
  `content` varchar(2000) DEFAULT NULL COMMENT '评论的内容',
  `remark` varchar(100) DEFAULT NULL COMMENT '备注（审核不通过时添加）',
  `support` int(10) unsigned DEFAULT '0' COMMENT '支持（赞）',
  `oppose` int(10) unsigned DEFAULT '0' COMMENT '反对（踩）',
  `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '添加时间',
  `update_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT COMMENT='文章评论';

-- ----------------------------
--  Table structure for `biz_tags`
-- ----------------------------
DROP TABLE IF EXISTS `biz_tags`;
CREATE TABLE `biz_tags` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(50) NOT NULL COMMENT '书签名',
  `description` varchar(100) DEFAULT NULL COMMENT '描述',
  `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '添加时间',
  `update_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间',
  `status` tinyint(1) unsigned DEFAULT '1' COMMENT '是否删除 1：启用 0:不启用',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT COMMENT='文章标签列表';

-- ----------------------------
--  Records of `biz_tags`
-- ----------------------------
BEGIN;
INSERT INTO `biz_tags` VALUES ('1', '多线程', '', '2019-01-28 14:10:48', '2019-01-28 14:10:48', '1'), ('2', '日记随笔', '', '2019-02-13 05:20:53', '2019-02-13 05:20:53', '1');
COMMIT;

-- ----------------------------
--  Table structure for `biz_type`
-- ----------------------------
DROP TABLE IF EXISTS `biz_type`;
CREATE TABLE `biz_type` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `pid` bigint(20) unsigned DEFAULT NULL,
  `pids` varchar(255) DEFAULT NULL COMMENT '父级ids',
  `description` varchar(200) DEFAULT NULL COMMENT '描述',
  `sort` int(10) DEFAULT NULL COMMENT '排序',
  `icon` varchar(100) DEFAULT NULL COMMENT '图标',
  `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '添加时间',
  `update_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间',
  `status` tinyint(1) unsigned DEFAULT '0' COMMENT '是否删除 1：启用 0:不启用',
  `name` varchar(50) DEFAULT NULL COMMENT '分类',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT COMMENT='文章类型表';

-- ----------------------------
--  Records of `biz_type`
-- ----------------------------
BEGIN;
INSERT INTO `biz_type` VALUES ('1', '0', null, '', '1', '', '2019-01-28 14:09:52', '2019-02-13 05:39:17', '1', 'java基础'), ('2', '1', null, '', '1', '', '2019-01-28 14:10:36', '2019-01-28 14:10:36', '1', '多线程'), ('3', '0', null, '', '0', '', '2019-02-13 05:20:35', '2019-02-13 05:38:49', '1', '随笔');
COMMIT;

-- ----------------------------
--  Table structure for `sys_config`
-- ----------------------------
DROP TABLE IF EXISTS `sys_config`;
CREATE TABLE `sys_config` (
  `id` int(20) unsigned NOT NULL AUTO_INCREMENT,
  `home_desc` varchar(255) DEFAULT NULL COMMENT '首页描述',
  `home_keywords` varchar(255) DEFAULT NULL COMMENT '首页关键字',
  `domain` varchar(255) DEFAULT NULL COMMENT '根域名',
  `cms_url` varchar(255) DEFAULT NULL COMMENT '管理系统的url',
  `site_url` varchar(255) DEFAULT NULL COMMENT '网站地址',
  `site_name` varchar(255) DEFAULT NULL COMMENT '站点名称',
  `site_desc` varchar(255) DEFAULT NULL COMMENT '站点描述',
  `site_favicon` varchar(255) DEFAULT NULL COMMENT '站点LOGO',
  `static_web_site` varchar(255) DEFAULT NULL COMMENT '资源文件（js、css等的路径）',
  `author_name` varchar(100) DEFAULT NULL COMMENT '站长名称',
  `author_email` varchar(100) DEFAULT NULL COMMENT '站长邮箱',
  `wx_code` varchar(255) DEFAULT NULL COMMENT '微信二维码地址',
  `qq` varchar(255) DEFAULT NULL COMMENT 'QQ',
  `weibo` varchar(255) DEFAULT NULL COMMENT '微博',
  `github` varchar(255) DEFAULT NULL COMMENT 'github',
  `maintenance` bit(1) DEFAULT NULL COMMENT '是否开启维护通知',
  `maintenance_data` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '系统维护时间',
  `comment` tinyint(1) unsigned DEFAULT '1' COMMENT '是否开启评论',
  `qiuniu_base_path` varchar(255) DEFAULT NULL COMMENT '七牛路径',
  `qiniu_access_key` varchar(100) DEFAULT NULL COMMENT '七牛ACCESS_KEY',
  `qiniu_secret_key` varchar(100) DEFAULT NULL COMMENT '七牛SECRET_KEY',
  `qiniu_bucket_name` varchar(100) DEFAULT NULL COMMENT '七牛BUCKET_NAME',
  `baidu_push_token` varchar(100) DEFAULT NULL COMMENT '百度推送的token',
  `wx_praise_code` varchar(200) DEFAULT NULL COMMENT '微信赞赏码',
  `zfb_praise_code` varchar(200) DEFAULT NULL COMMENT '支付宝赞赏码',
  `baidu_api_ak` varchar(100) DEFAULT NULL COMMENT '百度api授权AK(获取地址：http://lbsyun.baidu.com/apiconsole/key)',
  `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '添加时间',
  `update_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间',
  `websocket_url` varchar(255) DEFAULT NULL COMMENT 'websocket网址',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT COMMENT='系统配置';

-- ----------------------------
--  Records of `sys_config`
-- ----------------------------
BEGIN;
INSERT INTO `sys_config` VALUES ('1', '徐一炜个人的博客', 'xblog', 'itxyw.club', 'http://www.itxyw.club:8100', 'http://www.itxyw.club', '博客', '徐一炜个人的博客', 'http://www.itxyw.club/img/favicon.ico', 'http://www.itxyw.club/static', '徐一炜', '1328312923@qq.com', 'http://www.itxyw.club/static/img/qr/wx.jpeg', '1328312923', 'https://weibo.com/5341653123', 'https://github.com', b'0', '2018-12-19 23:07:52', '1', 'http://pc0fjx8p7.bkt.clouddn.com/', 'twCBIH6pc7bOsr5SXNcCKVGnoVNYGhX9YOvgi88H', 'z-g_6NcfdKI_mTHxKq0mr1sfpjTP_oanzAOLtAk1', 'itxywblog', null, null, null, null, '2018-01-19 23:07:52', '2018-06-12 12:03:28', 'ws://www.itxyw.club:8080/websocket');
COMMIT;

-- ----------------------------
--  Table structure for `sys_dept`
-- ----------------------------
DROP TABLE IF EXISTS `sys_dept`;
CREATE TABLE `sys_dept` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键id',
  `num` int(11) DEFAULT NULL COMMENT '排序',
  `pid` int(11) DEFAULT NULL COMMENT '父部门id',
  `pids` varchar(255) DEFAULT NULL COMMENT '父级ids',
  `simplename` varchar(45) DEFAULT NULL COMMENT '简称',
  `fullname` varchar(255) DEFAULT NULL COMMENT '全称',
  `tips` varchar(255) DEFAULT NULL COMMENT '提示',
  `version` int(11) DEFAULT NULL COMMENT '版本（乐观锁保留字段）',
  `status` tinyint(1) unsigned DEFAULT '1' COMMENT '是否删除 1：启用 0:不启用',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=25 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT COMMENT='部门表';

-- ----------------------------
--  Records of `sys_dept`
-- ----------------------------
BEGIN;
INSERT INTO `sys_dept` VALUES ('24', '1', '0', '[0],', '总公司', '总公司', '', null, '1');
COMMIT;

-- ----------------------------
--  Table structure for `sys_dict`
-- ----------------------------
DROP TABLE IF EXISTS `sys_dict`;
CREATE TABLE `sys_dict` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id',
  `num` int(11) DEFAULT NULL COMMENT '排序',
  `pid` int(11) DEFAULT NULL COMMENT '父级字典',
  `name` varchar(255) DEFAULT NULL COMMENT '名称',
  `tips` varchar(255) DEFAULT NULL COMMENT '提示',
  `code` varchar(255) DEFAULT NULL COMMENT '值',
  `status` tinyint(1) unsigned DEFAULT '1' COMMENT '是否删除 1：启用 0:不启用',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=60 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT COMMENT='字典表';

-- ----------------------------
--  Records of `sys_dict`
-- ----------------------------
BEGIN;
INSERT INTO `sys_dict` VALUES ('50', '0', '0', '性别', null, 'sys_sex', '1'), ('51', '1', '50', '男', null, '1', '1'), ('52', '2', '50', '女', null, '2', '1'), ('53', '0', '0', '状态', null, 'sys_state', '1'), ('54', '1', '53', '启用', null, '1', '1'), ('55', '2', '53', '禁用', null, '2', '1'), ('56', '0', '0', '账号状态', null, 'account_state', '1'), ('57', '1', '56', '启用', null, '1', '1'), ('58', '2', '56', '冻结', null, '2', '1'), ('59', '3', '56', '已删除', null, '3', '1');
COMMIT;

-- ----------------------------
--  Table structure for `sys_expense`
-- ----------------------------
DROP TABLE IF EXISTS `sys_expense`;
CREATE TABLE `sys_expense` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `money` decimal(20,2) DEFAULT NULL COMMENT '报销金额',
  `desc` varchar(255) DEFAULT '' COMMENT '描述',
  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  `state` int(11) DEFAULT NULL COMMENT '状态: 1.待提交  2:待审核   3.审核通过 4:驳回',
  `userid` int(11) DEFAULT NULL COMMENT '用户id',
  `processId` varchar(255) DEFAULT NULL COMMENT '流程定义id',
  `status` tinyint(1) unsigned DEFAULT '1' COMMENT '是否删除 1：启用 0:不启用',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT COMMENT='报销表';

-- ----------------------------
--  Table structure for `sys_link`
-- ----------------------------
DROP TABLE IF EXISTS `sys_link`;
CREATE TABLE `sys_link` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `url` varchar(200) NOT NULL COMMENT '链接地址',
  `name` varchar(50) DEFAULT NULL COMMENT '链接名',
  `description` varchar(300) DEFAULT NULL COMMENT '链接介绍',
  `email` varchar(100) DEFAULT NULL COMMENT '友链站长邮箱',
  `qq` varchar(13) DEFAULT NULL COMMENT '友链站长QQ',
  `favicon` varchar(255) DEFAULT NULL,
  `status` tinyint(1) unsigned DEFAULT '1' COMMENT '1:启用 2:禁用 0:删除',
  `home_page_display` tinyint(1) unsigned DEFAULT '1' COMMENT '是否首页显示 1:首页显示 2:不首页显示',
  `remark` varchar(100) DEFAULT NULL COMMENT '备注',
  `source` varchar(200) DEFAULT 'ADMIN' COMMENT 'ADMIN:管理员添加   AUTOMATIC:自动申请',
  `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '添加时间',
  `update_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=36 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT COMMENT='友情赞助';

-- ----------------------------
--  Records of `sys_link`
-- ----------------------------
BEGIN;
INSERT INTO `sys_link` VALUES ('35', 'http://music.zhyd.me', '我的音乐馆', '我的音乐馆', 'yadong.zhang0415@gmail.com', null, 'http://music.zhyd.me/images/favicon.ico', '1', null, null, 'ADMIN', '2018-04-19 20:01:35', '2018-04-19 20:01:35');
COMMIT;

-- ----------------------------
--  Table structure for `sys_login_log`
-- ----------------------------
DROP TABLE IF EXISTS `sys_login_log`;
CREATE TABLE `sys_login_log` (
  `id` int(65) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `logname` varchar(255) DEFAULT NULL COMMENT '日志名称',
  `userid` int(65) DEFAULT NULL COMMENT '管理员id',
  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  `succeed` varchar(255) DEFAULT NULL COMMENT '是否执行成功',
  `message` text COMMENT '具体消息',
  `ip` varchar(255) DEFAULT NULL COMMENT '登录ip',
  `status` int(11) DEFAULT '1' COMMENT '是否删除 1：启用 0:不启用',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=70 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT COMMENT='登录记录';

-- ----------------------------
--  Records of `sys_login_log`
-- ----------------------------
BEGIN;
INSERT INTO `sys_login_log` VALUES ('1', '登录日志', '1', '2019-01-21 08:40:28', '成功', null, '36.27.45.67', '1'), ('2', '登录日志', '1', '2019-01-22 06:02:41', '成功', null, '122.235.184.101', '1'), ('3', '登录失败日志', null, '2019-01-22 06:04:20', '成功', '账号:admins,账号密码错误', '122.235.184.101', '1'), ('4', '登录日志', '1', '2019-01-22 06:04:49', '成功', null, '122.235.184.101', '1'), ('5', '登录日志', '1', '2019-01-22 07:14:48', '成功', null, '122.235.184.101', '1'), ('6', '登录日志', '1', '2019-01-23 00:55:19', '成功', null, '122.235.184.101', '1'), ('7', '登录失败日志', null, '2019-01-25 02:01:59', '成功', '账号:admins,账号密码错误', '122.235.184.101', '1'), ('8', '登录日志', '1', '2019-01-25 02:02:06', '成功', null, '122.235.184.101', '1'), ('9', '登录日志', '1', '2019-01-28 13:31:38', '成功', null, '123.158.33.7', '1'), ('10', '登录失败日志', null, '2019-01-29 09:30:50', '成功', '账号:admins,账号密码错误', '60.186.203.46', '1'), ('11', '登录失败日志', null, '2019-01-29 09:30:55', '成功', '账号:admins,账号密码错误', '60.186.203.46', '1'), ('12', '登录日志', '1', '2019-01-29 09:31:03', '成功', null, '60.186.203.46', '1'), ('13', '退出日志', '1', '2019-01-29 09:31:52', '成功', null, '60.186.203.46', '1'), ('14', '登录日志', '1', '2019-01-29 09:31:58', '成功', null, '60.186.203.46', '1'), ('15', '登录日志', '1', '2019-01-30 02:59:27', '成功', null, '218.74.30.199', '1'), ('16', '登录失败日志', null, '2019-02-01 09:36:47', '成功', '账号:admins,账号密码错误', '113.214.210.71', '1'), ('17', '登录失败日志', null, '2019-02-01 09:36:47', '成功', '账号:admin,验证码错误', '113.214.210.71', '1'), ('18', '登录日志', '1', '2019-02-01 09:36:49', '成功', null, '113.214.210.71', '1'), ('19', '登录日志', '1', '2019-02-01 09:36:53', '成功', null, '113.214.210.71', '1'), ('20', '登录日志', '1', '2019-02-03 07:09:40', '成功', null, '113.214.194.27', '1'), ('21', '登录日志', '1', '2019-02-13 05:19:37', '成功', null, '122.233.194.154', '1'), ('22', '登录日志', '1', '2019-02-13 08:21:54', '成功', null, '122.233.194.154', '1'), ('23', '登录日志', '1', '2019-02-13 08:21:54', '成功', null, '122.233.194.154', '1'), ('24', '登录日志', '1', '2019-02-13 08:23:35', '成功', null, '122.233.194.154', '1'), ('25', '登录日志', '1', '2019-02-14 02:34:54', '成功', null, '122.233.194.154', '1'), ('26', '登录日志', '1', '2019-02-14 09:05:26', '成功', null, '122.233.194.154', '1'), ('27', '登录失败日志', null, '2019-02-15 06:45:56', '成功', '账号:admin,验证码错误', '218.74.29.236', '1'), ('28', '登录日志', '1', '2019-02-15 06:46:04', '成功', null, '218.74.29.236', '1'), ('29', '登录日志', '1', '2019-02-16 06:41:55', '成功', null, '218.74.29.236', '1'), ('30', '登录日志', '1', '2019-02-17 12:23:05', '成功', null, '101.71.194.117', '1'), ('31', '登录失败日志', null, '2019-02-22 08:31:58', '成功', '账号:admins,账号密码错误', '115.200.243.177', '1'), ('32', '登录失败日志', null, '2019-02-22 08:32:10', '成功', '账号:admins,账号密码错误', '115.200.243.177', '1'), ('33', '登录失败日志', null, '2019-02-22 08:32:24', '成功', '账号:admin,验证码错误', '115.200.243.177', '1'), ('34', '登录失败日志', null, '2019-02-22 08:32:36', '成功', '账号:admins,账号密码错误', '115.200.243.177', '1'), ('35', '登录日志', '1', '2019-02-22 08:32:46', '成功', null, '115.200.243.177', '1'), ('36', '登录失败日志', null, '2019-03-14 03:49:35', '成功', '账号:admins,账号密码错误', '183.159.239.40', '1'), ('37', '登录失败日志', null, '2019-03-14 03:49:44', '成功', '账号:admin,验证码错误', '183.159.239.40', '1'), ('38', '登录日志', '1', '2019-03-14 03:49:52', '成功', null, '183.159.239.40', '1'), ('39', '登录失败日志', null, '2019-05-07 02:52:35', '成功', '账号:admin,验证码错误', '36.24.128.185', '1'), ('40', '登录失败日志', null, '2019-05-07 02:53:00', '成功', '账号:1,账号密码错误', '61.151.179.27', '1'), ('41', '登录失败日志', null, '2019-05-07 02:53:00', '成功', '账号:1,账号密码错误', '124.160.219.28', '1'), ('42', '登录失败日志', null, '2019-05-08 03:13:35', '成功', '账号:admins,账号密码错误', '36.24.128.185', '1'), ('43', '登录失败日志', null, '2019-05-08 03:13:42', '成功', '账号:admins,账号密码错误', '36.24.128.185', '1'), ('44', '登录失败日志', null, '2019-05-08 03:13:48', '成功', '账号:admins,账号密码错误', '36.24.128.185', '1'), ('45', '登录失败日志', null, '2019-05-08 03:13:57', '成功', '账号:root,账号密码错误', '36.24.128.185', '1'), ('46', '登录失败日志', null, '2019-05-08 03:14:07', '成功', '账号:admins,账号密码错误', '36.24.128.185', '1'), ('47', '登录失败日志', null, '2019-05-08 03:14:13', '成功', '账号:admin,验证码错误', '36.24.128.185', '1'), ('48', '登录日志', '1', '2019-05-08 03:14:20', '成功', null, '36.24.128.185', '1'), ('49', '登录失败日志', null, '2019-05-29 03:50:11', '成功', '账号:admins,账号密码错误', '101.71.192.127', '1'), ('50', '登录日志', '1', '2019-05-29 03:50:16', '成功', null, '101.71.192.127', '1'), ('51', '登录失败日志', null, '2019-06-03 10:45:07', '成功', '账号:admins,账号密码错误', '123.158.32.44', '1'), ('52', '登录失败日志', null, '2019-06-03 10:45:11', '成功', '账号:admins,账号密码错误', '123.158.32.44', '1'), ('53', '登录日志', '1', '2019-06-03 10:45:16', '成功', null, '123.158.32.44', '1'), ('54', '登录日志', '1', '2019-06-04 06:53:25', '成功', null, '123.158.32.44', '1'), ('55', '登录失败日志', null, '2019-08-26 12:01:42', '成功', '账号:admin,验证码错误', '112.10.158.232', '1'), ('56', '登录失败日志', null, '2019-08-26 12:01:48', '成功', '账号:admin,验证码错误', '112.10.158.232', '1'), ('57', '登录失败日志', null, '2019-08-26 12:01:54', '成功', '账号:admin,验证码错误', '112.10.158.232', '1'), ('58', '登录失败日志', null, '2019-08-26 12:02:08', '成功', '账号:admin,验证码错误', '112.10.158.232', '1'), ('59', '登录失败日志', null, '2019-08-26 12:02:13', '成功', '账号:admin,验证码错误', '112.10.158.232', '1'), ('60', '登录失败日志', null, '2019-08-26 12:05:30', '成功', '账号:admin,验证码错误', '112.10.158.232', '1'), ('61', '登录失败日志', null, '2019-08-26 12:06:18', '成功', '账号:admin,验证码错误', '112.10.158.232', '1'), ('62', '登录失败日志', null, '2019-08-26 12:06:25', '成功', '账号:admin,验证码错误', '112.10.158.232', '1'), ('63', '登录失败日志', null, '2019-08-26 12:07:58', '成功', '账号:admin,验证码错误', '112.10.158.232', '1'), ('64', '登录失败日志', null, '2019-08-26 12:08:49', '成功', '账号:test,验证码错误', '112.10.158.232', '1'), ('65', '登录失败日志', null, '2019-08-26 12:09:06', '成功', '账号:aa,账号密码错误', '112.10.158.232', '1'), ('66', '登录失败日志', null, '2019-08-26 12:09:17', '成功', '账号:manager,验证码错误', '112.10.158.232', '1'), ('67', '登录失败日志', null, '2019-08-26 12:11:15', '成功', '账号:admin,验证码错误', '112.10.158.232', '1'), ('68', '登录失败日志', null, '2019-08-26 12:12:16', '成功', '账号:admin,验证码错误', '112.10.158.232', '1'), ('69', '登录日志', '1', '2019-08-26 12:14:01', '成功', null, '112.10.158.232', '1');
COMMIT;

-- ----------------------------
--  Table structure for `sys_menu`
-- ----------------------------
DROP TABLE IF EXISTS `sys_menu`;
CREATE TABLE `sys_menu` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键id',
  `code` varchar(255) DEFAULT NULL COMMENT '菜单编号',
  `pcode` varchar(255) DEFAULT NULL COMMENT '菜单父编号',
  `pcodes` varchar(255) DEFAULT NULL COMMENT '当前菜单的所有父菜单编号',
  `name` varchar(255) DEFAULT NULL COMMENT '菜单名称',
  `icon` varchar(255) DEFAULT NULL COMMENT '菜单图标',
  `url` varchar(255) DEFAULT NULL COMMENT 'url地址',
  `num` int(65) DEFAULT NULL COMMENT '菜单排序号',
  `levels` int(65) DEFAULT NULL COMMENT '菜单层级',
  `ismenu` bit(1) DEFAULT NULL COMMENT '是否是菜单（1：是  0：不是）',
  `tips` varchar(255) DEFAULT NULL COMMENT '备注',
  `status` int(65) DEFAULT NULL COMMENT '菜单状态 :  1:启用   0:不启用',
  `isopen` bit(1) DEFAULT NULL COMMENT '是否打开:    1:打开   0:不打开',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=190 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT COMMENT='菜单表';

-- ----------------------------
--  Records of `sys_menu`
-- ----------------------------
BEGIN;
INSERT INTO `sys_menu` VALUES ('20', 'article', '0', '[0],', '文章管理', 'fa-file-text-o', '#', '5', '1', b'1', null, '1', b'1'), ('21', 'biz_type', 'article', '[0],[article],', '文章分类', '', '/article/type', '99', '2', b'1', null, '1', b'0'), ('22', 'biz_type_list', 'biz_type', '[0],[article],[biz_type],', '文章分类列表', '', '/article/type/list', '99', '3', b'0', null, '1', b'0'), ('23', 'biz_type_add', 'biz_type', '[0],[article],[biz_type],', '文章分类添加', '', '/article/type/add', '99', '3', b'0', null, '1', b'0'), ('24', 'biz_type_update', 'biz_type', '[0],[article],[biz_type],', '文章分类更新', '', '/article/type/update', '99', '3', b'0', null, '1', b'0'), ('25', 'biz_type_delete', 'biz_type', '[0],[article],[biz_type],', '文章分类删除', '', '/article/type/delete', '99', '3', b'0', null, '1', b'0'), ('26', 'biz_type_detail', 'biz_type', '[0],[article],[biz_type],', '文章分类详情', '', '/article/type/detail', '99', '3', b'0', null, '1', b'0'), ('27', 'biz_tags', 'article', '[0],[article],', '文章标签', '', '/article/tags', '99', '2', b'1', null, '1', b'0'), ('28', 'biz_tags_list', 'biz_tags', '[0],[article],[biz_tags],', '文章标签列表', '', '/article/tags/list', '99', '3', b'0', null, '1', b'0'), ('29', 'biz_tags_add', 'biz_tags', '[0],[article],[biz_tags],', '文章标签添加', '', '/article/tags/add', '99', '3', b'0', null, '1', b'0'), ('30', 'biz_tags_update', 'biz_tags', '[0],[article],[biz_tags],', '文章标签更新', '', '/article/tags/update', '99', '3', b'0', null, '1', b'0'), ('31', 'biz_tags_delete', 'biz_tags', '[0],[article],[biz_tags],', '文章标签删除', '', '/article/tags/delete', '99', '3', b'0', null, '1', b'0'), ('32', 'biz_tags_detail', 'biz_tags', '[0],[article],[biz_tags],', '文章标签详情', '', '/article/tags/detail', '99', '3', b'0', null, '1', b'0'), ('33', 'biz_article', 'article', '[0],[article],', '文章管理', '', '/article/article', '99', '2', b'1', null, '1', b'0'), ('34', 'biz_article_list', 'biz_article', '[0],[article],[biz_article],', '文章列表', '', '/article/article/list', '99', '3', b'0', null, '1', b'0'), ('35', 'biz_article_add', 'biz_article', '[0],[article],[biz_article],', '发表文章', '', '/article/article/add', '99', '3', b'0', null, '1', b'0'), ('36', 'biz_article_update', 'biz_article', '[0],[article],[biz_article],', '修改文章', '', '/article/article/update', '99', '3', b'0', null, '1', b'0'), ('37', 'biz_article_delete', 'biz_article', '[0],[article],[biz_article],', '删除文章', '', '/article/article/delete', '99', '3', b'0', null, '1', b'0'), ('38', 'biz_article_detail', 'biz_article', '[0],[article],[biz_article],', '文章详情', '', '/article/article/detail', '99', '3', b'0', null, '1', b'0'), ('40', 'web_manager', '0', '[0],', '网站管理', 'fa-television', '#', '6', '1', b'1', null, '1', b'1'), ('105', 'system', '0', '[0],', '系统管理', 'fa-user', '#', '4', '1', b'1', null, '1', b'1'), ('106', 'mgr', 'system', '[0],[system],', '用户管理', '', '/mgr', '1', '2', b'1', null, '1', b'0'), ('107', 'mgr_add', 'mgr', '[0],[system],[mgr],', '添加用户', null, '/mgr/add', '1', '3', b'0', null, '1', b'0'), ('108', 'mgr_edit', 'mgr', '[0],[system],[mgr],', '修改用户', null, '/mgr/edit', '2', '3', b'0', null, '1', b'0'), ('109', 'mgr_delete', 'mgr', '[0],[system],[mgr],', '删除用户', null, '/mgr/delete', '3', '3', b'0', null, '1', b'0'), ('110', 'mgr_reset', 'mgr', '[0],[system],[mgr],', '重置密码', null, '/mgr/reset', '4', '3', b'0', null, '1', b'0'), ('111', 'mgr_freeze', 'mgr', '[0],[system],[mgr],', '冻结用户', null, '/mgr/freeze', '5', '3', b'0', null, '1', b'0'), ('112', 'mgr_unfreeze', 'mgr', '[0],[system],[mgr],', '解除冻结用户', null, '/mgr/unfreeze', '6', '3', b'0', null, '1', b'0'), ('113', 'mgr_setRole', 'mgr', '[0],[system],[mgr],', '分配角色', null, '/mgr/setRole', '7', '3', b'0', null, '1', b'0'), ('114', 'role', 'system', '[0],[system],', '角色管理', null, '/role', '2', '2', b'1', null, '1', b'0'), ('115', 'role_add', 'role', '[0],[system],[role],', '添加角色', null, '/role/add', '1', '3', b'0', null, '1', b'0'), ('116', 'role_edit', 'role', '[0],[system],[role],', '修改角色', null, '/role/edit', '2', '3', b'0', null, '1', b'0'), ('117', 'role_remove', 'role', '[0],[system],[role],', '删除角色', null, '/role/remove', '3', '3', b'0', null, '1', b'0'), ('118', 'role_setAuthority', 'role', '[0],[system],[role],', '配置权限', null, '/role/setAuthority', '4', '3', b'0', null, '1', b'0'), ('119', 'menu', 'system', '[0],[system],', '菜单管理', null, '/menu', '4', '2', b'1', null, '1', b'0'), ('120', 'menu_add', 'menu', '[0],[system],[menu],', '添加菜单', null, '/menu/add', '1', '3', b'0', null, '1', b'0'), ('121', 'menu_edit', 'menu', '[0],[system],[menu],', '修改菜单', null, '/menu/edit', '2', '3', b'0', null, '1', b'0'), ('122', 'menu_remove', 'menu', '[0],[system],[menu],', '删除菜单', null, '/menu/remove', '3', '3', b'0', null, '1', b'0'), ('128', 'log', 'system', '[0],[system],', '业务日志', null, '/log', '6', '2', b'1', null, '1', b'0'), ('130', 'druid', 'system', '[0],[system],', '监控管理', null, '/druid', '7', '2', b'1', null, '1', null), ('131', 'dept', 'system', '[0],[system],', '部门管理', null, '/dept', '3', '2', b'1', null, '1', null), ('132', 'dict', 'system', '[0],[system],', '字典管理', null, '/dict', '4', '2', b'1', null, '1', null), ('133', 'loginLog', 'system', '[0],[system],', '登录日志', null, '/loginLog', '6', '2', b'1', null, '1', null), ('134', 'log_clean', 'log', '[0],[system],[log],', '清空日志', null, '/log/delLog', '3', '3', b'0', null, '1', null), ('135', 'dept_add', 'dept', '[0],[system],[dept],', '添加部门', null, '/dept/add', '1', '3', b'0', null, '1', null), ('136', 'dept_update', 'dept', '[0],[system],[dept],', '修改部门', null, '/dept/update', '1', '3', b'0', null, '1', null), ('137', 'dept_delete', 'dept', '[0],[system],[dept],', '删除部门', null, '/dept/delete', '1', '3', b'0', null, '1', null), ('138', 'dict_add', 'dict', '[0],[system],[dict],', '添加字典', null, '/dict/add', '1', '3', b'0', null, '1', null), ('139', 'dict_update', 'dict', '[0],[system],[dict],', '修改字典', null, '/dict/update', '1', '3', b'0', null, '1', null), ('140', 'dict_delete', 'dict', '[0],[system],[dict],', '删除字典', null, '/dict/delete', '1', '3', b'0', null, '1', null), ('141', 'notice', 'notice_manager', '[0],[web_manager],[notice_manager],', '消息列表', '', '/notice', '9', '2', b'1', null, '1', null), ('144', 'notice_delete', 'notice', '[0],[web_manager],[notice_manager],[notice],', '删除通知', null, '/notice/delete', '3', '3', b'0', null, '1', null), ('145', 'send_admin_msg', 'notice_manager', '[0],[web_manager],[notice_manager],', '推送消息', 'fa-rocket', '/notice/send', '1', '2', b'1', null, '1', null), ('148', 'code', '0', '[0],', '代码生成', 'fa-code', '/code', '3', '1', b'1', null, '1', null), ('149', 'api_mgr', '0', '[0],', '接口文档', 'fa-leaf', '/swagger-ui.html', '2', '1', b'1', null, '1', null), ('150', 'to_menu_edit', 'menu', '[0],[system],[menu],', '菜单编辑跳转', '', '/menu/menu_edit', '4', '3', b'0', null, '1', null), ('151', 'menu_list', 'menu', '[0],[system],[menu],', '菜单列表', '', '/menu/list', '5', '3', b'0', null, '1', null), ('152', 'to_dept_update', 'dept', '[0],[system],[dept],', '修改部门跳转', '', '/dept/dept_update', '4', '3', b'0', null, '1', null), ('153', 'dept_list', 'dept', '[0],[system],[dept],', '部门列表', '', '/dept/list', '5', '3', b'0', null, '1', null), ('154', 'dept_detail', 'dept', '[0],[system],[dept],', '部门详情', '', '/dept/detail', '6', '3', b'0', null, '1', null), ('155', 'to_dict_edit', 'dict', '[0],[system],[dict],', '修改菜单跳转', '', '/dict/dict_edit', '4', '3', b'0', null, '1', null), ('156', 'dict_list', 'dict', '[0],[system],[dict],', '字典列表', '', '/dict/list', '5', '3', b'0', null, '1', null), ('157', 'dict_detail', 'dict', '[0],[system],[dict],', '字典详情', '', '/dict/detail', '6', '3', b'0', null, '1', null), ('158', 'log_list', 'log', '[0],[system],[log],', '日志列表', '', '/log/list', '2', '3', b'0', null, '1', null), ('159', 'log_detail', 'log', '[0],[system],[log],', '日志详情', '', '/log/detail', '3', '3', b'0', null, '1', null), ('160', 'del_login_log', 'loginLog', '[0],[system],[loginLog],', '清空登录日志', '', '/loginLog/delLoginLog', '1', '3', b'0', null, '1', null), ('161', 'login_log_list', 'loginLog', '[0],[system],[loginLog],', '登录日志列表', '', '/loginLog/list', '2', '3', b'0', null, '1', null), ('162', 'to_role_edit', 'role', '[0],[system],[role],', '修改角色跳转', '', '/role/role_edit', '5', '3', b'0', null, '1', null), ('163', 'to_role_assign', 'role', '[0],[system],[role],', '角色分配跳转', '', '/role/role_assign', '6', '3', b'0', null, '1', null), ('164', 'role_list', 'role', '[0],[system],[role],', '角色列表', '', '/role/list', '7', '3', b'0', null, '1', null), ('165', 'to_assign_role', 'mgr', '[0],[system],[mgr],', '分配角色跳转', '', '/mgr/role_assign', '8', '3', b'0', null, '1', null), ('166', 'to_user_edit', 'mgr', '[0],[system],[mgr],', '编辑用户跳转', '', '/mgr/user_edit', '9', '3', b'0', null, '1', null), ('167', 'mgr_list', 'mgr', '[0],[system],[mgr],', '用户列表', '', '/mgr/list', '10', '3', b'0', null, '1', null), ('168', 'biz_article_top', 'biz_article', '[0],[article],[biz_article],', '置顶文章', '', '/article/article/top', '99', '3', b'0', null, '1', b'0'), ('169', 'biz_article_recommend', 'biz_article', '[0],[article],[biz_article],', '推荐文章', '', '/article/article/recommend', '99', '3', b'0', null, '1', b'0'), ('170', 'link', 'web_manager', '[0],[web_manager],', '友情链接', '', '/link', '99', '2', b'1', null, '1', b'0'), ('171', 'link_list', 'link', '[0],[web_manager],[link],', '友情链接列表', '', '/link/list', '99', '3', b'0', null, '1', b'0'), ('172', 'link_add', 'link', '[0],[web_manager],[link],', '友情链接添加', '', '/link/add', '99', '3', b'0', null, '1', b'0'), ('174', 'link_delete', 'link', '[0],[web_manager],[link],', '友情链接删除', '', '/link/delete', '99', '3', b'0', null, '1', b'0'), ('178', 'link_update', 'link', '[0],[web_manager],[link],', '友情链接更新', '', '/link/update', '99', '3', b'0', null, '1', b'0'), ('179', 'template', 'web_manager', '[0],[web_manager],', '系统模板', '', '/template', '99', '2', b'1', null, '1', b'0'), ('180', 'template_list', 'template', '[0],[web_manager],[template],', '系统模板列表', '', '/template/list', '99', '3', b'0', null, '1', b'0'), ('181', 'template_add', 'template', '[0],[web_manager],[template],', '系统模板添加', '', '/template/add', '99', '3', b'0', null, '1', b'0'), ('182', 'template_delete', 'template', '[0],[web_manager],[template],', '系统模板删除', '', '/template/delete', '99', '3', b'0', null, '1', b'0'), ('183', 'template_update', 'template', '[0],[web_manager],[template],', '系统模板更新', '', '/template/update', '99', '3', b'0', null, '1', b'0'), ('184', 'biz_comment', 'article', '[0],[article],', '文章评论', '', '/article/comment', '99', '2', b'1', null, '1', b'0'), ('185', 'biz_comment_list', 'biz_comment', '[0],[article],[biz_comment],', '文章评论列表', '', '/article/comment/list', '99', '3', b'0', null, '1', b'0'), ('186', 'biz_comment_check', 'biz_comment', '[0],[article],[biz_comment],', '文章评论审核', '', '/article/comment/check', '99', '3', b'0', null, '1', b'0'), ('187', 'biz_comment_reply', 'biz_comment', '[0],[article],[biz_comment],', '文章评论回复', '', '/article/comment/reply', '99', '3', b'0', null, '1', b'0'), ('188', 'biz_comment_delete', 'biz_comment', '[0],[article],[biz_comment],', '文章评论删除', '', '/article/comment/delete', '99', '3', b'0', null, '1', b'0'), ('189', 'notice_manager', 'web_manager', '[0],[web_manager],', '消息管理', '', '#', '99', '1', b'1', null, '1', b'0');
COMMIT;

-- ----------------------------
--  Table structure for `sys_notice`
-- ----------------------------
DROP TABLE IF EXISTS `sys_notice`;
CREATE TABLE `sys_notice` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
  `title` varchar(255) DEFAULT NULL COMMENT '标题',
  `type` varchar(30) DEFAULT NULL COMMENT '类型',
  `content` text COMMENT '内容',
  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  `user_id` bigint(20) unsigned DEFAULT NULL COMMENT '创建人',
  `status` tinyint(1) unsigned DEFAULT '1' COMMENT '是否删除 1：启用 0:不启用',
  `to_user_id` bigint(20) unsigned DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT COMMENT='通知表';

-- ----------------------------
--  Table structure for `sys_operation_log`
-- ----------------------------
DROP TABLE IF EXISTS `sys_operation_log`;
CREATE TABLE `sys_operation_log` (
  `id` int(65) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `logtype` varchar(255) DEFAULT NULL COMMENT '日志类型',
  `logname` varchar(255) DEFAULT NULL COMMENT '日志名称',
  `userid` int(65) DEFAULT NULL COMMENT '用户id',
  `classname` varchar(255) DEFAULT NULL COMMENT '类名称',
  `method` text COMMENT '方法名称',
  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  `succeed` varchar(255) DEFAULT NULL COMMENT '是否成功',
  `message` text COMMENT '备注',
  `status` tinyint(1) unsigned DEFAULT '1' COMMENT '是否删除 1：启用 0:不启用',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT COMMENT='操作日志';

-- ----------------------------
--  Records of `sys_operation_log`
-- ----------------------------
BEGIN;
INSERT INTO `sys_operation_log` VALUES ('1', '异常日志', '', '1', null, null, '2019-01-22 06:05:22', '失败', 'java.lang.NullPointerException\n	at com.xuyiwei.xblog.core.util.ToolUtil.getWebRootPath(ToolUtil.java:559)\n	at com.xuyiwei.xblog.modular.code.factory.DefaultTemplateFactory.getDefaultParams(DefaultTemplateFactory.java:45)\n	at com.xuyiwei.xblog.modular.code.controller.CodeController.blackboard(CodeController.java:42)\n	at com.xuyiwei.xblog.modular.code.controller.CodeControllerTTFastClassBySpringCGLIBTT7206d7df.invoke(<generated>)\n	at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)\n	at org.springframework.aop.framework.CglibAopProxyTCglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:747)\n	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)\n	at org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:89)\n	at com.xuyiwei.xblog.core.intercept.SessionHolderInterceptor.sessionKit(SessionHolderInterceptor.java:29)\n	at sun.reflect.GeneratedMethodAccessor123.invoke(Unknown Source)\n	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)\n	at java.lang.reflect.Method.invoke(Method.java:498)\n	at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:644)\n	at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:633)\n	at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:70)\n	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)\n	at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)\n	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)\n	at org.springframework.aop.framework.CglibAopProxyTDynamicAdvisedInterceptor.intercept(CglibAopProxy.java:689)\n	at com.xuyiwei.xblog.modular.code.controller.CodeControllerTTEnhancerBySpringCGLIBTT47333878.blackboard(<generated>)\n	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)\n	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)\n	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)\n	at java.lang.reflect.Method.invoke(Method.java:498)\n	at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:209)\n	at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:136)\n	at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:102)\n	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:877)\n	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:783)\n	at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)\n	at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:991)\n	at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:925)\n	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:974)\n	at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:866)\n	at javax.servlet.http.HttpServlet.service(HttpServlet.java:635)\n	at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:851)\n	at javax.servlet.http.HttpServlet.service(HttpServlet.java:742)\n	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)\n	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n	at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)\n	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n	at org.apache.shiro.web.servlet.ProxiedFilterChain.doFilter(ProxiedFilterChain.java:61)\n	at org.apache.shiro.web.servlet.AdviceFilter.executeChain(AdviceFilter.java:108)\n	at org.apache.shiro.web.servlet.AdviceFilter.doFilterInternal(AdviceFilter.java:137)\n	at org.apache.shiro.web.servlet.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:125)\n	at org.apache.shiro.web.servlet.ProxiedFilterChain.doFilter(ProxiedFilterChain.java:66)\n	at org.apache.shiro.web.servlet.AbstractShiroFilter.executeChain(AbstractShiroFilter.java:449)\n	at org.apache.shiro.web.servlet.AbstractShiroFilterT1.call(AbstractShiroFilter.java:365)\n	at org.apache.shiro.subject.support.SubjectCallable.doCall(SubjectCallable.java:90)\n	at org.apache.shiro.subject.support.SubjectCallable.call(SubjectCallable.java:83)\n	at org.apache.shiro.subject.support.DelegatingSubject.execute(DelegatingSubject.java:387)\n	at org.apache.shiro.web.servlet.AbstractShiroFilter.doFilterInternal(AbstractShiroFilter.java:362)\n	at org.apache.shiro.web.servlet.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:125)\n	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n	at com.xuyiwei.xblog.core.xss.XssFilter.doFilter(XssFilter.java:31)\n	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n	at com.alibaba.druid.support.http.WebStatFilter.doFilter(WebStatFilter.java:123)\n	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n	at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99)\n	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)\n	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n	at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:109)\n	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)\n	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n	at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:81)\n	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)\n	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n	at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200)\n	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)\n	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198)\n	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)\n	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:496)\n	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140)\n	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:81)\n	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)\n	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342)\n	at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:803)\n	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)\n	at org.apache.coyote.AbstractProtocolTConnectionHandler.process(AbstractProtocol.java:790)\n	at org.apache.tomcat.util.net.NioEndpointTSocketProcessor.doRun(NioEndpoint.java:1459)\n	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)\n	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)\n	at java.util.concurrent.ThreadPoolExecutorTWorker.run(ThreadPoolExecutor.java:624)\n	at org.apache.tomcat.util.threads.TaskThreadTWrappingRunnable.run(TaskThread.java:61)\n	at java.lang.Thread.run(Thread.java:748)\n', '1'), ('2', '异常日志', '', '1', null, null, '2019-01-22 07:23:52', '失败', 'java.lang.NullPointerException\n	at com.xuyiwei.xblog.core.util.ToolUtil.getWebRootPath(ToolUtil.java:559)\n	at com.xuyiwei.xblog.modular.code.factory.DefaultTemplateFactory.getDefaultParams(DefaultTemplateFactory.java:45)\n	at com.xuyiwei.xblog.modular.code.controller.CodeController.blackboard(CodeController.java:42)\n	at com.xuyiwei.xblog.modular.code.controller.CodeControllerTTFastClassBySpringCGLIBTT7206d7df.invoke(<generated>)\n	at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)\n	at org.springframework.aop.framework.CglibAopProxyTCglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:747)\n	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)\n	at org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:89)\n	at com.xuyiwei.xblog.core.intercept.SessionHolderInterceptor.sessionKit(SessionHolderInterceptor.java:29)\n	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)\n	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)\n	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)\n	at java.lang.reflect.Method.invoke(Method.java:498)\n	at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:644)\n	at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:633)\n	at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:70)\n	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)\n	at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)\n	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)\n	at org.springframework.aop.framework.CglibAopProxyTDynamicAdvisedInterceptor.intercept(CglibAopProxy.java:689)\n	at com.xuyiwei.xblog.modular.code.controller.CodeControllerTTEnhancerBySpringCGLIBTT47333878.blackboard(<generated>)\n	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)\n	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)\n	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)\n	at java.lang.reflect.Method.invoke(Method.java:498)\n	at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:209)\n	at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:136)\n	at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:102)\n	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:877)\n	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:783)\n	at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)\n	at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:991)\n	at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:925)\n	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:974)\n	at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:866)\n	at javax.servlet.http.HttpServlet.service(HttpServlet.java:635)\n	at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:851)\n	at javax.servlet.http.HttpServlet.service(HttpServlet.java:742)\n	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)\n	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n	at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)\n	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n	at org.apache.shiro.web.servlet.ProxiedFilterChain.doFilter(ProxiedFilterChain.java:61)\n	at org.apache.shiro.web.servlet.AdviceFilter.executeChain(AdviceFilter.java:108)\n	at org.apache.shiro.web.servlet.AdviceFilter.doFilterInternal(AdviceFilter.java:137)\n	at org.apache.shiro.web.servlet.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:125)\n	at org.apache.shiro.web.servlet.ProxiedFilterChain.doFilter(ProxiedFilterChain.java:66)\n	at org.apache.shiro.web.servlet.AbstractShiroFilter.executeChain(AbstractShiroFilter.java:449)\n	at org.apache.shiro.web.servlet.AbstractShiroFilterT1.call(AbstractShiroFilter.java:365)\n	at org.apache.shiro.subject.support.SubjectCallable.doCall(SubjectCallable.java:90)\n	at org.apache.shiro.subject.support.SubjectCallable.call(SubjectCallable.java:83)\n	at org.apache.shiro.subject.support.DelegatingSubject.execute(DelegatingSubject.java:387)\n	at org.apache.shiro.web.servlet.AbstractShiroFilter.doFilterInternal(AbstractShiroFilter.java:362)\n	at org.apache.shiro.web.servlet.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:125)\n	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n	at com.xuyiwei.xblog.core.xss.XssFilter.doFilter(XssFilter.java:31)\n	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n	at com.alibaba.druid.support.http.WebStatFilter.doFilter(WebStatFilter.java:123)\n	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n	at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99)\n	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)\n	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n	at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:109)\n	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)\n	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n	at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:81)\n	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)\n	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n	at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200)\n	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)\n	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198)\n	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)\n	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:496)\n	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140)\n	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:81)\n	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)\n	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342)\n	at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:803)\n	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)\n	at org.apache.coyote.AbstractProtocolTConnectionHandler.process(AbstractProtocol.java:790)\n	at org.apache.tomcat.util.net.NioEndpointTSocketProcessor.doRun(NioEndpoint.java:1459)\n	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)\n	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)\n	at java.util.concurrent.ThreadPoolExecutorTWorker.run(ThreadPoolExecutor.java:624)\n	at org.apache.tomcat.util.threads.TaskThreadTWrappingRunnable.run(TaskThread.java:61)\n	at java.lang.Thread.run(Thread.java:748)\n', '1'), ('3', '业务日志', '重置管理员密码', '1', 'com.xuyiwei.xblog.modular.system.controller.UserMgrController', 'reset', '2019-01-29 09:31:48', '成功', 'null=null', '1'), ('4', '业务日志', '修改管理员', '1', 'com.xuyiwei.xblog.modular.system.controller.UserMgrController', 'edit', '2019-02-13 08:26:59', '成功', '账号=admin;;;', '1'), ('5', '业务日志', '修改管理员', '1', 'com.xuyiwei.xblog.modular.system.controller.UserMgrController', 'edit', '2019-02-13 08:26:59', '成功', '账号=admin;;;', '1'), ('6', '业务日志', '修改管理员', '1', 'com.xuyiwei.xblog.modular.system.controller.UserMgrController', 'edit', '2019-02-13 08:27:03', '成功', '账号=admin;;;', '1'), ('7', '异常日志', '', '1', null, null, '2019-08-26 12:14:17', '失败', 'java.lang.NullPointerException\n	at com.xuyiwei.xblog.core.util.ToolUtil.getWebRootPath(ToolUtil.java:559)\n	at com.xuyiwei.xblog.modular.code.factory.DefaultTemplateFactory.getDefaultParams(DefaultTemplateFactory.java:45)\n	at com.xuyiwei.xblog.modular.code.controller.CodeController.blackboard(CodeController.java:42)\n	at com.xuyiwei.xblog.modular.code.controller.CodeControllerTTFastClassBySpringCGLIBTT7206d7df.invoke(<generated>)\n	at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)\n	at org.springframework.aop.framework.CglibAopProxyTCglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:747)\n	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)\n	at org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:89)\n	at com.xuyiwei.xblog.core.intercept.SessionHolderInterceptor.sessionKit(SessionHolderInterceptor.java:29)\n	at sun.reflect.GeneratedMethodAccessor132.invoke(Unknown Source)\n	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)\n	at java.lang.reflect.Method.invoke(Method.java:498)\n	at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:644)\n	at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:633)\n	at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:70)\n	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)\n	at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)\n	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)\n	at org.springframework.aop.framework.CglibAopProxyTDynamicAdvisedInterceptor.intercept(CglibAopProxy.java:689)\n	at com.xuyiwei.xblog.modular.code.controller.CodeControllerTTEnhancerBySpringCGLIBTT2eb6b50e.blackboard(<generated>)\n	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)\n	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)\n	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)\n	at java.lang.reflect.Method.invoke(Method.java:498)\n	at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:209)\n	at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:136)\n	at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:102)\n	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:877)\n	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:783)\n	at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)\n	at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:991)\n	at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:925)\n	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:974)\n	at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:866)\n	at javax.servlet.http.HttpServlet.service(HttpServlet.java:635)\n	at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:851)\n	at javax.servlet.http.HttpServlet.service(HttpServlet.java:742)\n	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)\n	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n	at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)\n	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n	at org.apache.shiro.web.servlet.ProxiedFilterChain.doFilter(ProxiedFilterChain.java:61)\n	at org.apache.shiro.web.servlet.AdviceFilter.executeChain(AdviceFilter.java:108)\n	at org.apache.shiro.web.servlet.AdviceFilter.doFilterInternal(AdviceFilter.java:137)\n	at org.apache.shiro.web.servlet.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:125)\n	at org.apache.shiro.web.servlet.ProxiedFilterChain.doFilter(ProxiedFilterChain.java:66)\n	at org.apache.shiro.web.servlet.AbstractShiroFilter.executeChain(AbstractShiroFilter.java:449)\n	at org.apache.shiro.web.servlet.AbstractShiroFilterT1.call(AbstractShiroFilter.java:365)\n	at org.apache.shiro.subject.support.SubjectCallable.doCall(SubjectCallable.java:90)\n	at org.apache.shiro.subject.support.SubjectCallable.call(SubjectCallable.java:83)\n	at org.apache.shiro.subject.support.DelegatingSubject.execute(DelegatingSubject.java:387)\n	at org.apache.shiro.web.servlet.AbstractShiroFilter.doFilterInternal(AbstractShiroFilter.java:362)\n	at org.apache.shiro.web.servlet.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:125)\n	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n	at com.xuyiwei.xblog.core.xss.XssFilter.doFilter(XssFilter.java:31)\n	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n	at com.alibaba.druid.support.http.WebStatFilter.doFilter(WebStatFilter.java:123)\n	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n	at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99)\n	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)\n	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n	at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:109)\n	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)\n	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n	at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:81)\n	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)\n	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n	at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200)\n	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)\n	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198)\n	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)\n	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:496)\n	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140)\n	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:81)\n	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)\n	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342)\n	at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:803)\n	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)\n	at org.apache.coyote.AbstractProtocolTConnectionHandler.process(AbstractProtocol.java:790)\n	at org.apache.tomcat.util.net.NioEndpointTSocketProcessor.doRun(NioEndpoint.java:1459)\n	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)\n	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)\n	at java.util.concurrent.ThreadPoolExecutorTWorker.run(ThreadPoolExecutor.java:624)\n	at org.apache.tomcat.util.threads.TaskThreadTWrappingRunnable.run(TaskThread.java:61)\n	at java.lang.Thread.run(Thread.java:748)\n', '1'), ('8', '异常日志', '', '1', null, null, '2019-08-26 12:14:20', '失败', 'java.lang.NullPointerException\n	at com.xuyiwei.xblog.core.util.ToolUtil.getWebRootPath(ToolUtil.java:559)\n	at com.xuyiwei.xblog.modular.code.factory.DefaultTemplateFactory.getDefaultParams(DefaultTemplateFactory.java:45)\n	at com.xuyiwei.xblog.modular.code.controller.CodeController.blackboard(CodeController.java:42)\n	at com.xuyiwei.xblog.modular.code.controller.CodeControllerTTFastClassBySpringCGLIBTT7206d7df.invoke(<generated>)\n	at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)\n	at org.springframework.aop.framework.CglibAopProxyTCglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:747)\n	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)\n	at org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:89)\n	at com.xuyiwei.xblog.core.intercept.SessionHolderInterceptor.sessionKit(SessionHolderInterceptor.java:29)\n	at sun.reflect.GeneratedMethodAccessor132.invoke(Unknown Source)\n	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)\n	at java.lang.reflect.Method.invoke(Method.java:498)\n	at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:644)\n	at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:633)\n	at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:70)\n	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)\n	at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)\n	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)\n	at org.springframework.aop.framework.CglibAopProxyTDynamicAdvisedInterceptor.intercept(CglibAopProxy.java:689)\n	at com.xuyiwei.xblog.modular.code.controller.CodeControllerTTEnhancerBySpringCGLIBTT2eb6b50e.blackboard(<generated>)\n	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)\n	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)\n	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)\n	at java.lang.reflect.Method.invoke(Method.java:498)\n	at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:209)\n	at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:136)\n	at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:102)\n	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:877)\n	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:783)\n	at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)\n	at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:991)\n	at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:925)\n	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:974)\n	at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:866)\n	at javax.servlet.http.HttpServlet.service(HttpServlet.java:635)\n	at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:851)\n	at javax.servlet.http.HttpServlet.service(HttpServlet.java:742)\n	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)\n	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n	at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)\n	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n	at org.apache.shiro.web.servlet.ProxiedFilterChain.doFilter(ProxiedFilterChain.java:61)\n	at org.apache.shiro.web.servlet.AdviceFilter.executeChain(AdviceFilter.java:108)\n	at org.apache.shiro.web.servlet.AdviceFilter.doFilterInternal(AdviceFilter.java:137)\n	at org.apache.shiro.web.servlet.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:125)\n	at org.apache.shiro.web.servlet.ProxiedFilterChain.doFilter(ProxiedFilterChain.java:66)\n	at org.apache.shiro.web.servlet.AbstractShiroFilter.executeChain(AbstractShiroFilter.java:449)\n	at org.apache.shiro.web.servlet.AbstractShiroFilterT1.call(AbstractShiroFilter.java:365)\n	at org.apache.shiro.subject.support.SubjectCallable.doCall(SubjectCallable.java:90)\n	at org.apache.shiro.subject.support.SubjectCallable.call(SubjectCallable.java:83)\n	at org.apache.shiro.subject.support.DelegatingSubject.execute(DelegatingSubject.java:387)\n	at org.apache.shiro.web.servlet.AbstractShiroFilter.doFilterInternal(AbstractShiroFilter.java:362)\n	at org.apache.shiro.web.servlet.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:125)\n	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n	at com.xuyiwei.xblog.core.xss.XssFilter.doFilter(XssFilter.java:31)\n	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n	at com.alibaba.druid.support.http.WebStatFilter.doFilter(WebStatFilter.java:123)\n	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n	at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99)\n	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)\n	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n	at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:109)\n	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)\n	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n	at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:81)\n	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)\n	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n	at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200)\n	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)\n	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198)\n	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)\n	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:496)\n	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140)\n	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:81)\n	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)\n	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342)\n	at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:803)\n	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)\n	at org.apache.coyote.AbstractProtocolTConnectionHandler.process(AbstractProtocol.java:790)\n	at org.apache.tomcat.util.net.NioEndpointTSocketProcessor.doRun(NioEndpoint.java:1459)\n	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)\n	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)\n	at java.util.concurrent.ThreadPoolExecutorTWorker.run(ThreadPoolExecutor.java:624)\n	at org.apache.tomcat.util.threads.TaskThreadTWrappingRunnable.run(TaskThread.java:61)\n	at java.lang.Thread.run(Thread.java:748)\n', '1'), ('9', '异常日志', '', '1', null, null, '2019-08-26 12:14:23', '失败', 'java.lang.NullPointerException\n	at com.xuyiwei.xblog.core.util.ToolUtil.getWebRootPath(ToolUtil.java:559)\n	at com.xuyiwei.xblog.modular.code.factory.DefaultTemplateFactory.getDefaultParams(DefaultTemplateFactory.java:45)\n	at com.xuyiwei.xblog.modular.code.controller.CodeController.blackboard(CodeController.java:42)\n	at com.xuyiwei.xblog.modular.code.controller.CodeControllerTTFastClassBySpringCGLIBTT7206d7df.invoke(<generated>)\n	at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)\n	at org.springframework.aop.framework.CglibAopProxyTCglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:747)\n	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)\n	at org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:89)\n	at com.xuyiwei.xblog.core.intercept.SessionHolderInterceptor.sessionKit(SessionHolderInterceptor.java:29)\n	at sun.reflect.GeneratedMethodAccessor132.invoke(Unknown Source)\n	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)\n	at java.lang.reflect.Method.invoke(Method.java:498)\n	at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:644)\n	at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:633)\n	at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:70)\n	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)\n	at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)\n	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)\n	at org.springframework.aop.framework.CglibAopProxyTDynamicAdvisedInterceptor.intercept(CglibAopProxy.java:689)\n	at com.xuyiwei.xblog.modular.code.controller.CodeControllerTTEnhancerBySpringCGLIBTT2eb6b50e.blackboard(<generated>)\n	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)\n	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)\n	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)\n	at java.lang.reflect.Method.invoke(Method.java:498)\n	at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:209)\n	at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:136)\n	at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:102)\n	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:877)\n	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:783)\n	at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)\n	at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:991)\n	at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:925)\n	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:974)\n	at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:866)\n	at javax.servlet.http.HttpServlet.service(HttpServlet.java:635)\n	at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:851)\n	at javax.servlet.http.HttpServlet.service(HttpServlet.java:742)\n	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)\n	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n	at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)\n	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n	at org.apache.shiro.web.servlet.ProxiedFilterChain.doFilter(ProxiedFilterChain.java:61)\n	at org.apache.shiro.web.servlet.AdviceFilter.executeChain(AdviceFilter.java:108)\n	at org.apache.shiro.web.servlet.AdviceFilter.doFilterInternal(AdviceFilter.java:137)\n	at org.apache.shiro.web.servlet.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:125)\n	at org.apache.shiro.web.servlet.ProxiedFilterChain.doFilter(ProxiedFilterChain.java:66)\n	at org.apache.shiro.web.servlet.AbstractShiroFilter.executeChain(AbstractShiroFilter.java:449)\n	at org.apache.shiro.web.servlet.AbstractShiroFilterT1.call(AbstractShiroFilter.java:365)\n	at org.apache.shiro.subject.support.SubjectCallable.doCall(SubjectCallable.java:90)\n	at org.apache.shiro.subject.support.SubjectCallable.call(SubjectCallable.java:83)\n	at org.apache.shiro.subject.support.DelegatingSubject.execute(DelegatingSubject.java:387)\n	at org.apache.shiro.web.servlet.AbstractShiroFilter.doFilterInternal(AbstractShiroFilter.java:362)\n	at org.apache.shiro.web.servlet.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:125)\n	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n	at com.xuyiwei.xblog.core.xss.XssFilter.doFilter(XssFilter.java:31)\n	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n	at com.alibaba.druid.support.http.WebStatFilter.doFilter(WebStatFilter.java:123)\n	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n	at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99)\n	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)\n	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n	at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:109)\n	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)\n	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n	at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:81)\n	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)\n	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n	at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200)\n	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)\n	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198)\n	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)\n	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:496)\n	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140)\n	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:81)\n	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)\n	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342)\n	at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:803)\n	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)\n	at org.apache.coyote.AbstractProtocolTConnectionHandler.process(AbstractProtocol.java:790)\n	at org.apache.tomcat.util.net.NioEndpointTSocketProcessor.doRun(NioEndpoint.java:1459)\n	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)\n	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)\n	at java.util.concurrent.ThreadPoolExecutorTWorker.run(ThreadPoolExecutor.java:624)\n	at org.apache.tomcat.util.threads.TaskThreadTWrappingRunnable.run(TaskThread.java:61)\n	at java.lang.Thread.run(Thread.java:748)\n', '1');
COMMIT;

-- ----------------------------
--  Table structure for `sys_relation`
-- ----------------------------
DROP TABLE IF EXISTS `sys_relation`;
CREATE TABLE `sys_relation` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `menuid` bigint(11) DEFAULT NULL COMMENT '菜单id',
  `roleid` int(11) DEFAULT NULL COMMENT '角色id',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6046 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT COMMENT='角色和菜单关联表';

-- ----------------------------
--  Records of `sys_relation`
-- ----------------------------
BEGIN;
INSERT INTO `sys_relation` VALUES ('3377', '105', '5'), ('3378', '106', '5'), ('3379', '107', '5'), ('3380', '108', '5'), ('3381', '109', '5'), ('3382', '110', '5'), ('3383', '111', '5'), ('3384', '112', '5'), ('3385', '113', '5'), ('3386', '114', '5'), ('3387', '115', '5'), ('3388', '116', '5'), ('3389', '117', '5'), ('3390', '118', '5'), ('3391', '119', '5'), ('3392', '120', '5'), ('3393', '121', '5'), ('3394', '122', '5'), ('3395', '150', '5'), ('3396', '151', '5'), ('5955', '20', '1'), ('5956', '21', '1'), ('5957', '22', '1'), ('5958', '23', '1'), ('5959', '24', '1'), ('5960', '25', '1'), ('5961', '26', '1'), ('5962', '27', '1'), ('5963', '28', '1'), ('5964', '29', '1'), ('5965', '30', '1'), ('5966', '31', '1'), ('5967', '32', '1'), ('5968', '33', '1'), ('5969', '34', '1'), ('5970', '35', '1'), ('5971', '36', '1'), ('5972', '37', '1'), ('5973', '38', '1'), ('5974', '168', '1'), ('5975', '169', '1'), ('5976', '184', '1'), ('5977', '185', '1'), ('5978', '186', '1'), ('5979', '187', '1'), ('5980', '188', '1'), ('5981', '40', '1'), ('5982', '170', '1'), ('5983', '171', '1'), ('5984', '172', '1'), ('5985', '174', '1'), ('5986', '178', '1'), ('5987', '179', '1'), ('5988', '180', '1'), ('5989', '181', '1'), ('5990', '182', '1'), ('5991', '183', '1'), ('5992', '189', '1'), ('5993', '141', '1'), ('5994', '144', '1'), ('5995', '145', '1'), ('5996', '105', '1'), ('5997', '106', '1'), ('5998', '107', '1'), ('5999', '108', '1'), ('6000', '109', '1'), ('6001', '110', '1'), ('6002', '111', '1'), ('6003', '112', '1'), ('6004', '113', '1'), ('6005', '165', '1'), ('6006', '166', '1'), ('6007', '167', '1'), ('6008', '114', '1'), ('6009', '115', '1'), ('6010', '116', '1'), ('6011', '117', '1'), ('6012', '118', '1'), ('6013', '162', '1'), ('6014', '163', '1'), ('6015', '164', '1'), ('6016', '119', '1'), ('6017', '120', '1'), ('6018', '121', '1'), ('6019', '122', '1'), ('6020', '150', '1'), ('6021', '151', '1'), ('6022', '128', '1'), ('6023', '134', '1'), ('6024', '158', '1'), ('6025', '159', '1'), ('6026', '130', '1'), ('6027', '131', '1'), ('6028', '135', '1'), ('6029', '136', '1'), ('6030', '137', '1'), ('6031', '152', '1'), ('6032', '153', '1'), ('6033', '154', '1'), ('6034', '132', '1'), ('6035', '138', '1'), ('6036', '139', '1'), ('6037', '140', '1'), ('6038', '155', '1'), ('6039', '156', '1'), ('6040', '157', '1'), ('6041', '133', '1'), ('6042', '160', '1'), ('6043', '161', '1'), ('6044', '148', '1'), ('6045', '149', '1');
COMMIT;

-- ----------------------------
--  Table structure for `sys_role`
-- ----------------------------
DROP TABLE IF EXISTS `sys_role`;
CREATE TABLE `sys_role` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键id',
  `num` int(11) DEFAULT NULL COMMENT '序号',
  `pid` int(11) DEFAULT NULL COMMENT '父角色id',
  `name` varchar(255) DEFAULT NULL COMMENT '角色名称',
  `deptid` int(11) DEFAULT NULL COMMENT '部门名称',
  `tips` varchar(255) DEFAULT NULL COMMENT '提示',
  `version` int(11) DEFAULT NULL COMMENT '保留字段(暂时没用）',
  `status` tinyint(1) unsigned DEFAULT '1' COMMENT '是否删除 1：启用 0:不启用',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT COMMENT='角色表';

-- ----------------------------
--  Records of `sys_role`
-- ----------------------------
BEGIN;
INSERT INTO `sys_role` VALUES ('1', '1', '0', '超级管理员', '24', 'administrator', '1', '1'), ('5', '2', '1', '临时', '26', 'temp', null, '1');
COMMIT;

-- ----------------------------
--  Table structure for `sys_template`
-- ----------------------------
DROP TABLE IF EXISTS `sys_template`;
CREATE TABLE `sys_template` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `ref_key` varchar(100) DEFAULT NULL COMMENT '键',
  `ref_value` text COMMENT '模板内容',
  `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '添加时间',
  `update_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT COMMENT='系统模板表';

-- ----------------------------
--  Records of `sys_template`
-- ----------------------------
BEGIN;
INSERT INTO `sys_template` VALUES ('1', 'TM_SITEMAP_XML', '<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<urlset xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\" xsi:schemaLocation=\"http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd\">\r\n	<url>\r\n		<loc>${config.siteUrl!}</loc>\r\n		<priority>1.0</priority>\r\n		<lastmod>${date(),dateFormat=\"yyyy-MM-dd\"}</lastmod>\r\n		<changefreq>daily</changefreq>\r\n	</url>\r\n	<url>\r\n		<loc>${config.siteUrl!}/about</loc>\r\n		<priority>0.6</priority>\r\n		<lastmod>${date(),dateFormat=\"yyyy-MM-dd\"}</lastmod>\r\n		<changefreq>daily</changefreq>\r\n	</url>\r\n	<url>\r\n		<loc>${config.siteUrl!}/links</loc>\r\n		<priority>0.6</priority>\r\n		<lastmod>${date(),dateFormat=\"yyyy-MM-dd\"}</lastmod>\r\n		<changefreq>daily</changefreq>\r\n	</url>\r\n	<url>\r\n		<loc>${config.siteUrl!}/guestbook</loc>\r\n		<priority>0.6</priority>\r\n		<lastmod>${date(),dateFormat=\"yyyy-MM-dd\"}</lastmod>\r\n		<changefreq>daily</changefreq>\r\n	</url>\r\n	<url>\r\n		<loc>${config.siteUrl!}/updateLog</loc>\r\n		<priority>0.6</priority>\r\n		<lastmod>${date(),dateFormat=\"yyyy-MM-dd\"}</lastmod>\r\n		<changefreq>daily</changefreq>\r\n	</url>\r\n	<url>\r\n		<loc>${config.siteUrl!}/recommended</loc>\r\n		<priority>0.6</priority>\r\n		<lastmod>${date(),dateFormat=\"yyyy-MM-dd\"}</lastmod>\r\n		<changefreq>daily</changefreq>\r\n	</url>\r\n	<%for(item in articleList!){\r\n		<url>\r\n			<loc>${config.siteUrl!}/article/${item.id}</loc>\r\n			<priority>0.6</priority>\r\n			<lastmod>${item.updateTime!,dateFormat=\"yyyy-MM-dd\"}</lastmod>\r\n			<changefreq>daily</changefreq>\r\n		</url>\r\n	}%>\r\n	<%for(item in articleTypeList!){\r\n	   <url>\r\n			<loc>${config.siteUrl!}/type/${item.id}</loc>\r\n			<priority>0.6</priority>\r\n			<lastmod>${item.updateTime!,dateFormat=\"yyyy-MM-dd\"}</lastmod>\r\n			<changefreq>daily</changefreq>\r\n		</url>\r\n	}%>\r\n	<%for(item in articleTagsList!){\r\n	   <url>\r\n			<loc>${config.siteUrl!}/tag/${item.id}</loc>\r\n			<priority>0.6</priority>\r\n			<lastmod>${item.updateTime!,dateFormat=\"yyyy-MM-dd\"}</lastmod>\r\n			<changefreq>daily</changefreq>\r\n		</url>\r\n	}%>\r\n</urlset>', '2018-01-19 10:47:06', '2018-03-13 17:41:31'), ('2', 'TM_SITEMAP_TXT', '${config.siteUrl!}\r\n${config.siteUrl!}/about\r\n${config.siteUrl!}/links\r\n${config.siteUrl!}/guestbook\r\n${config.siteUrl!}/updateLog\r\n${config.siteUrl!}/recommended\r\n<%for(item in articleList!){\r\n${config.siteUrl!}/article/${item.id!}\r\n}%>\r\n<%for(item in articleTypeList!){\r\n${config.siteUrl}/type/${item.id}\r\n}%>\r\n<%for(item in articleTagsList!){\r\n${config.siteUrl}/tag/${item.id}\r\n}%>', '2018-01-19 10:47:12', '2018-02-28 12:11:59'), ('3', 'TM_SITEMAP_HTML', '<!DOCTYPE html>\r\n<html lang=\"zh-CN\">\r\n<head>\r\n    <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/>\r\n    <title>${config.siteName!} 网站地图</title>\r\n    <meta name=\"author\" content=\"SiteMapX.com\"/>\r\n    <meta name=\"robots\" content=\"index,follow\"/>\r\n    <style type=\"text/css\">\r\n        body {\r\n            color: #000000;\r\n            background: #ffffff;\r\n            margin: 20px;\r\n            font-family: Verdana, Arial, Helvetica, sans-serif;\r\n            font-size: 12px;\r\n        }\r\n\r\n        #myTable {\r\n            list-style: none;\r\n            margin: 10px 0px 10px 0px;\r\n            padding: 0px;\r\n            width: 100%;\r\n            min-width: 804px;\r\n        }\r\n\r\n        #myTable li {\r\n            list-style-type: none;\r\n            width: 100%;\r\n            min-width: 404px;\r\n            height: 20px;\r\n            line-height: 20px;\r\n        }\r\n\r\n        .pull-left{\r\n            float: left!important;\r\n        }\r\n        .pull-right{\r\n            float: right!important;\r\n        }\r\n\r\n        #myTable li .T1-h {\r\n            font-weight: bold;\r\n            min-width: 300px;\r\n        }\r\n\r\n        #myTable li .T2-h {\r\n            width: 200px;\r\n            font-weight: bold;\r\n        }\r\n\r\n        #myTable li .T3-h {\r\n            width: 200px;\r\n            font-weight: bold;\r\n        }\r\n\r\n        #myTable li .T4-h {\r\n            width: 100px;\r\n            font-weight: bold;\r\n        }\r\n\r\n        #myTable li .T1 {\r\n            min-width: 300px;\r\n        }\r\n\r\n        #myTable li .T2 {\r\n            width: 200px;\r\n        }\r\n\r\n        #myTable li .T3 {\r\n            width: 200px;\r\n        }\r\n\r\n        #myTable li .T4 {\r\n            width: 100px;\r\n        }\r\n\r\n        #footer {\r\n            padding: 2px;\r\n            margin: 0px;\r\n            font-size: 8pt;\r\n            color: gray;\r\n            min-width: 900px;\r\n        }\r\n\r\n        #footer a {\r\n            color: gray;\r\n        }\r\n\r\n        .myClear {\r\n            clear: both;\r\n        }\r\n\r\n        #nav, #content, #footer {padding: 8px; border: 1px solid #EEEEEE; clear: both; width: 95%; margin: auto; margin-top: 10px;}\r\n\r\n    </style>\r\n</head>\r\n<body>\r\n<h2 style=\"text-align: center; margin-top: 20px\">${config.siteName!} 网站地图 </h2>\r\n<div id=\"nav\"><a href=\"${config.siteUrl!}\"><strong>${config.siteName!}</strong></a> &raquo; <a href=\"${config.siteUrl!}/sitemap.html\">站点地图</a></div>\r\n<div id=\"content\">\r\n    <h3>最新文章</h3>\r\n    <ul id=\"myTable\">\r\n        <li>\r\n            <div class=\"T1-h pull-left\">URL</div>\r\n            <div class=\"T2-h pull-right\">Last Change</div>\r\n            <div class=\"T3-h pull-right\">Change Frequency</div>\r\n            <div class=\"T4-h pull-right\">Priority</div>\r\n        </li>\r\n        <div class=\"myClear\"></div>\r\n        <li>\r\n            <div class=\"T1 pull-left\"><a href=\"${config.siteUrl!}\" title=\"${config.siteName!}\">${config.siteName!} | 一个程序员的个人博客</a></div>\r\n            <div class=\"T2 pull-right\">${date(),dateFormat=\"yyyy-MM-dd\"}</div>\r\n            <div class=\"T3 pull-right\">daily</div>\r\n            <div class=\"T4 pull-right\">1</div>\r\n        </li>\r\n        <div class=\"myClear\"></div>\r\n        <li>\r\n            <div class=\"T1 pull-left\"><a href=\"${config.siteUrl!}/about\" title=\"${config.siteName!}\">关于 | ${config.siteName!}</a></div>\r\n            <div class=\"T2 pull-right\">${date(),dateFormat=\"yyyy-MM-dd\"}</div>\r\n            <div class=\"T3 pull-right\">daily</div>\r\n            <div class=\"T4 pull-right\">0.6</div>\r\n        </li>\r\n        <div class=\"myClear\"></div>\r\n        <li>\r\n            <div class=\"T1 pull-left\"><a href=\"${config.siteUrl!}/links\" title=\"${config.siteName!}\">友情链接 | ${config.siteName!}</a></div>\r\n            <div class=\"T2 pull-right\">${date(),dateFormat=\"yyyy-MM-dd\"}</div>\r\n            <div class=\"T3 pull-right\">daily</div>\r\n            <div class=\"T4 pull-right\">0.6</div>\r\n        </li>\r\n        <div class=\"myClear\"></div>\r\n        <li>\r\n            <div class=\"T1 pull-left\"><a href=\"${config.siteUrl!}/guestbook\" title=\"${config.siteName!}\">留言板 | ${config.siteName!}</a></div>\r\n            <div class=\"T2 pull-right\">${date(),dateFormat=\"yyyy-MM-dd\"}</div>\r\n            <div class=\"T3 pull-right\">daily</div>\r\n            <div class=\"T4 pull-right\">0.6</div>\r\n        </li>\r\n        <div class=\"myClear\"></div>\r\n        <li>\r\n            <div class=\"T1 pull-left\"><a href=\"${config.siteUrl!}/updateLog\" title=\"${config.siteName!}\">网站更新记录 | ${config.siteName!}</a></div>\r\n            <div class=\"T2 pull-right\">${date(),dateFormat=\"yyyy-MM-dd\"}</div>\r\n            <div class=\"T3 pull-right\">daily</div>\r\n            <div class=\"T4 pull-right\">0.6</div>\r\n        </li>\r\n		<div class=\"myClear\"></div>\r\n        <li>\r\n            <div class=\"T1 pull-left\"><a href=\"${config.siteUrl!}/recommended\" title=\"${config.siteName!}\">站长推荐 | ${config.siteName!}</a></div>\r\n            <div class=\"T2 pull-right\">${date(),dateFormat=\"yyyy-MM-dd\"}</div>\r\n            <div class=\"T3 pull-right\">daily</div>\r\n            <div class=\"T4 pull-right\">0.6</div>\r\n        </li>\r\n        <div class=\"myClear\"></div>\r\n        <%for(item in articleList!){\r\n            <li>\r\n                <div class=\"T1 pull-left\"><a href=\"${config.siteUrl!}/article/${item.id}\" title=\"${item.title!}\">${item.title!} | ${config.siteName!}</a></div>\r\n                <div class=\"T2 pull-right\">${date(),dateFormat=\"yyyy-MM-dd\"}</div>\r\n                <div class=\"T3 pull-right\">daily</div>\r\n                <div class=\"T4 pull-right\">0.6</div>\r\n            </li>\r\n            <div class=\"myClear\"></div>\r\n        }%>\r\n    </ul>\r\n</div>\r\n<div id=\"content\">\r\n    <h3>分类目录</h3>\r\n    <ul id=\"myTable\">\r\n        <%for(item in articleTypeList!){\r\n            <li>\r\n                <div class=\"T1 pull-left\"><a href=\"${config.siteUrl!}/type/${item.id!}\" title=\"${item.name!}\">${item.name!} | ${config.siteName!}</a></div>\r\n                <div class=\"T2 pull-right\">${item.updateTime!,dateFormat=\"yyyy-MM-dd\"}</div>\r\n                <div class=\"T3 pull-right\">daily</div>\r\n                <div class=\"T4 pull-right\">0.6</div>\r\n            </li>\r\n            <div class=\"myClear\"></div>\r\n        }%>\r\n    </ul>\r\n</div>\r\n<div id=\"content\">\r\n    <h3>标签目录</h3>\r\n    <ul id=\"myTable\">\r\n        <%for(item in articleTagsList!){\r\n            <li>\r\n                <div class=\"T1 pull-left\"><a href=\"${config.siteUrl!}/tag/${item.id!}\" title=\"${item.name!}\">${item.name!} | ${config.siteName!}</a></div>\r\n                <div class=\"T2 pull-right\">${item.updateTime!,dateFormat=\"yyyy-MM-dd\"}</div>\r\n                <div class=\"T3 pull-right\">daily</div>\r\n                <div class=\"T4 pull-right\">0.6</div>\r\n            </li>\r\n            <div class=\"myClear\"></div>\r\n        }%>\r\n    </ul>\r\n</div>\r\n<div id=\"footer\">\r\n    该文件由<a href=\"${config.siteUrl!}\" title=\"${config.siteName!}\">${config.siteName!}</a>网站自动生成。\r\n</div>\r\n</body>\r\n</html>', '2018-01-19 10:47:43', '2018-03-13 17:40:52'), ('4', 'TM_ROBOTS', 'Crawl-delay: 5\r\nSitemap: https://www.zhyd.me/sitemap.txt\r\nSitemap: https://www.zhyd.me/sitemap.xml\r\nSitemap: https://www.zhyd.me/sitemap.html\r\n', '2018-01-19 10:48:27', '2018-01-19 10:48:27'), ('5', 'TM_LINKS', '<!DOCTYPE html>\r\n<html lang=\"en\">\r\n<head>\r\n    <meta charset=\"UTF-8\">\r\n    <title>友情链接操作通知</title>\r\n</head>\r\n<body>\r\n<div style=\"border-radius:5px;font-size:13px;width:680px;font-family:微软雅黑,\'Helvetica Neue\',Arial,sans-serif;margin:10px auto 0px;border:1px solid #eee;max-width:100%\">\r\n    <div style=\"width:100%;background:#2f889a;color:#ffffff;border-radius:5px 5px 0 0\">\r\n        <p style=\"font-size:15px;word-break:break-all;padding:20px 32px;margin:0\">\r\n            友情链接操作通知\r\n        </p>\r\n    </div>\r\n    <div style=\"margin:0px auto;width:90%\">\r\n        <p>站长<a href=\"${link.url!}\" target=\"_blank\">${link.name!}</a>，您好!</p>\r\n        <p>您于 ${link.createTime!,dateFormat=\"yyyy-MM-dd\"} 提交的友链申请已通过系统审核。以下为您提交的信息，请确认（如有误，请联系我）！</p>\r\n        <p>\r\n        <ul>\r\n            <li>${link.name!}</li>\r\n            <li>${link.url!}</li>\r\n            <li>${link.description!}</li>\r\n            <li>${link.email!}</li>\r\n            <li>${link.qq!}</li>\r\n            <li><img src=\"${link.favicon!}\" width=\"100\" alt=\"LOGO\"></li>\r\n        </ul>\r\n        </p>\r\n        <p>本站会不定期检查连接有效性，如果因为贵站改版、服务到期等原因导致无法正常访问的，我会暂时停掉贵站友链，待贵站可以正常访问后，本站会继续启用贵站友链。</p>\r\n        <p>特别注意：以下情况，本站将在不做任何通知下，<strong>取消友链</strong>！</p>\r\n        <ul>\r\n            <li>私自取消本站友情链接</li>\r\n            <li>更换域名且未通知本站</li>\r\n            <li>网站内容长期不更新</li>\r\n            <li>友链上使用诸如nofollow之类的属性</li>\r\n        </ul>\r\n        <p>感谢您对 <a style=\"text-decoration:none;\" href=\"${config.siteUrl!}\" target=\"_blank\">${config.siteName!}</a> 的关注，如您有任何疑问，欢迎来我网站<a style=\"text-decoration:none;\" href=\"${config.siteUrl!}/guestbook\" target=\"_blank\">留言</a>。</p>\r\n        <p>（注：此邮件由系统自动发出，请勿回复。）</p>\r\n    </div>\r\n    <div class=\"adL\">\r\n    </div>\r\n</div>\r\n</body>\r\n</html>', '2018-01-19 10:48:54', '2018-01-19 10:48:54'), ('6', 'TM_COMMENT_AUDIT', '<!DOCTYPE html>\r\n<html lang=\"en\">\r\n<head>\r\n    <meta charset=\"UTF-8\">\r\n    <title>评论审核结果通知</title>\r\n</head>\r\n<body>\r\n<div style=\"border-radius:5px;font-size:13px;width:680px;font-family:微软雅黑,\'Helvetica Neue\',Arial,sans-serif;margin:10px auto 0px;border:1px solid #eee;max-width:100%\">\r\n    <div style=\"width:100%;background:#2f889a;color:#ffffff;border-radius:5px 5px 0 0\">\r\n        <p style=\"font-size:15px;word-break:break-all;padding:20px 32px;margin:0\">\r\n            评论审核结果通知\r\n        </p>\r\n    </div>\r\n    <div style=\"margin:0px auto;width:90%\">\r\n        <p>${comment.nickName!}，您好!</p>\r\n        <p>\r\n            您于 ${comment.createTime!,dateFormat=\"yyyy-MM-dd\"} 在文章《${config.siteUrl!}${comment.sourceUrl!}》 上发表的<span class=\"il\">评论</span>：\r\n        </p>\r\n        <div style=\"background:#efefef;margin:15px 0px;padding:1px 15px;border-radius:5px;font-size:14px;color:#333\">${comment.content!}</div>\r\n        <%if(comment.status! == \'APPROVED\'){%>\r\n        <p>已通过管理员审核并显示。</p>\r\n        <p>\r\n            您可以点击 <a style=\"text-decoration:none;\" href=\"${config.siteUrl!}${comment.sourceUrl!}\" target=\"_blank\">链接</a>查看回复的完整內容。\r\n        </p>\r\n        <%}else if(comment.status! == \'REJECT\'){%>\r\n        <p>审核失败！失败原因：</p>\r\n        <p style=\"background:#efefef;margin:15px 0px;padding:1px 15px;border-radius:5px;font-size:14px;color:#333\">${comment.remark!}</p>\r\n        <%}else if(comment.status! == \'DELETED\'){%>        <p>已被管理员删除！删除原因：</p>\r\n        <p style=\"background:#efefef;margin:15px 0px;padding:1px 15px;border-radius:5px;font-size:14px;color:#333\">${comment.remark!}</p>\r\n        <%}else{%>\r\n        <p>管理员正在审核中！审核通过后将给您及时发送通知！</p>\r\n        <%}%>\r\n        <p>感谢您对 <a style=\"text-decoration:none;\" href=\"${config.siteUrl!}\" target=\"_blank\">${config.siteName!}</a> 的关注，如您有任何疑问，欢迎来我网站<a style=\"text-decoration:none;\" href=\"${config.siteUrl!}/guestbook\" target=\"_blank\">留言</a>。</p>\r\n        <p>（注：此邮件由系统自动发出，请勿回复。）</p>\r\n    </div>\r\n    <div class=\"adL\">\r\n    </div>\r\n</div>\r\n</body>\r\n</html>', '2018-01-26 21:17:38', '2018-01-26 21:17:38'), ('7', 'TM_COMMENT_REPLY', '<!DOCTYPE html>\r\n<html lang=\"en\">\r\n<head>\r\n    <meta charset=\"UTF-8\">\r\n    <title>评论回复通知</title>\r\n</head>\r\n<body>\r\n<div style=\"border-radius:5px;font-size:13px;width:680px;font-family:微软雅黑,\'Helvetica Neue\',Arial,sans-serif;margin:10px auto 0px;border:1px solid #eee;max-width:100%\">\r\n    <div style=\"width:100%;background:#2f889a;color:#ffffff;border-radius:5px 5px 0 0\">\r\n        <p style=\"font-size:15px;word-break:break-all;padding:20px 32px;margin:0\">\r\n            评论回复通知\r\n        </p>\r\n    </div>\r\n    <div style=\"margin:0px auto;width:90%\">\r\n        <p>${comment.nickName!}，您好!</p>\r\n        <p>\r\n            您于 ${comment.createTime!,dateFormat=\"yyyy-MM-dd\"} 在文章《${config.siteUrl!}${comment.sourceUrl!}》 上发表的<span class=\"il\">评论</span>：\r\n        </p>\r\n        <div style=\"background:#efefef;margin:15px 0px;padding:1px 15px;border-radius:5px;font-size:14px;color:#333\">${comment.content!}</div>\r\n        <p>\r\n            有了 <strong>新的回复</strong>！您可以点击 <a style=\"text-decoration:none;\" href=\"${config.siteUrl!}${comment.sourceUrl!}\" target=\"_blank\">链接</a>查看回复的完整內容。\r\n        </p>\r\n        <p>感谢您对 <a style=\"text-decoration:none;\" href=\"${config.siteUrl!}\" target=\"_blank\">${config.siteName!}</a> 的关注，如您有任何疑问，欢迎来我网站<a style=\"text-decoration:none;\" href=\"${config.siteUrl!}/guestbook\" target=\"_blank\">留言</a>。</p>\r\n        <p>（注：此邮件由系统自动发出，请勿回复。）</p>\r\n    </div>\r\n    <div class=\"adL\">\r\n    </div>\r\n</div>\r\n</body>\r\n</html>', '2018-01-26 21:17:55', '2018-01-26 21:17:55'), ('8', 'TM_LINKS_TO_ADMIN', '<!DOCTYPE html>\r\n<html lang=\"en\">\r\n<head>\r\n    <meta charset=\"UTF-8\">\r\n    <title>友情链接操作通知</title>\r\n</head>\r\n<body>\r\n<div style=\"border-radius:5px;font-size:13px;width:680px;font-family:微软雅黑,\'Helvetica Neue\',Arial,sans-serif;margin:10px auto 0px;border:1px solid #eee;max-width:100%\">\r\n    <div style=\"width:100%;background:#2f889a;color:#ffffff;border-radius:5px 5px 0 0\">\r\n        <p style=\"font-size:15px;word-break:break-all;padding:20px 32px;margin:0\">\r\n            友情链接操作通知\r\n        </p>\r\n    </div>\r\n    <div style=\"margin:0px auto;width:90%\">\r\n        <p>有新的友情链接加入，信息如下</p>\r\n        <p>\r\n        <ul>\r\n            <li>${link.name!}</li>\r\n            <li>${link.url!}</li>\r\n            <li>${link.description!}</li>\r\n            <%if(link.favicon! !=null){%> <li><img src=\"${link.favicon!}\" width=\"100\" alt=\"LOGO\"></li><%}%>\r\n        </ul>\r\n        </p>\r\n        <p><a style=\"text-decoration:none;\" href=\"http://admin.zhyd.me\" target=\"_blank\">去后台继续审核</a>。</p>\r\n        <p>（注：此邮件由系统自动发出，请勿回复。）</p>\r\n    </div>\r\n    <div class=\"adL\">\r\n    </div>\r\n</div>\r\n</body>\r\n</html>', '2018-03-31 14:14:11', '2018-03-31 14:14:11'), ('9', 'TM_NEW_COMMENT', '<!DOCTYPE html>\r\n<html lang=\"en\">\r\n<head>\r\n    <meta charset=\"UTF-8\">\r\n    <title>新评论通知</title>\r\n</head>\r\n<body>\r\n<div style=\"border-radius:5px;font-size:13px;width:680px;font-family:微软雅黑,\'Helvetica Neue\',Arial,sans-serif;margin:10px auto 0px;border:1px solid #eee;max-width:100%\">\r\n    <div style=\"width:100%;background:#2f889a;color:#ffffff;border-radius:5px 5px 0 0\">\r\n        <p style=\"font-size:15px;word-break:break-all;padding:20px 32px;margin:0\">\r\n            新评论通知\r\n        </p>\r\n    </div>\r\n    <div style=\"margin:0px auto;width:90%\">\r\n        <p>\r\n            评论内容：\r\n        </p>\r\n        <div style=\"background:#efefef;margin:15px 0px;padding:1px 15px;border-radius:5px;font-size:14px;color:#333\"><%if(comment! !=null){%>${comment.content!}<%}else{%>**无评论内容**<%}%></div>\r\n        <p>\r\n            <a style=\"text-decoration:none;\" href=\"${config.siteUrl!}${comment.sourceUrl!}\" target=\"_blank\">查看完整评论</a>\r\n        </p>\r\n        <p>（注：此邮件由系统自动发出，请勿回复。）</p>\r\n    </div>\r\n    <div class=\"adL\">\r\n    </div>\r\n</div>\r\n</body>\r\n</html>', '2018-03-31 14:19:04', '2018-03-31 14:19:04'), ('10', 'TM_EMAIL_CODE', '<!DOCTYPE html>\r\n<html lang=\"en\">\r\n<head>\r\n    <meta charset=\"UTF-8\">\r\n    <title>徐一炜的个人博客的验证码</title>\r\n</head>\r\n<body>\r\n<div style=\"border-radius:5px;font-size:13px;width:680px;font-family:微软雅黑,\'Helvetica Neue\',Arial,sans-serif;margin:10px auto 0px;border:1px solid #eee;max-width:100%\">\r\n    <div style=\"width:100%;background:#2f889a;color:#ffffff;border-radius:5px 5px 0 0\">\r\n        <p style=\"font-size:15px;word-break:break-all;padding:20px 32px;margin:0\">\r\n            徐一炜的个人博客的验证码\r\n        </p>\r\n    </div>\r\n    <div style=\"margin:0px auto;width:90%\">\r\n        <p>\r\n            验证码：\r\n        </p>\r\n        <div style=\"background:#efefef;margin:15px 0px;padding:1px 15px;border-radius:5px;font-size:14px;color:#333\">${code!}</div>\r\n        <p>（注：此邮件由系统自动发出，请勿回复。）</p>\r\n    </div>\r\n    <div class=\"adL\">\r\n    </div>\r\n</div>\r\n</body>\r\n</html>', '2018-03-31 14:19:04', '2018-03-31 14:19:04');
COMMIT;

-- ----------------------------
--  Table structure for `sys_update_recorde`
-- ----------------------------
DROP TABLE IF EXISTS `sys_update_recorde`;
CREATE TABLE `sys_update_recorde` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `version` varchar(20) DEFAULT NULL COMMENT '更新版本',
  `description` varchar(2500) DEFAULT NULL COMMENT '更新记录备注',
  `recorde_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '项目更新时间',
  `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '添加时间',
  `update_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT;

-- ----------------------------
--  Table structure for `sys_user`
-- ----------------------------
DROP TABLE IF EXISTS `sys_user`;
CREATE TABLE `sys_user` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键id',
  `avatar` varchar(255) DEFAULT NULL COMMENT '头像',
  `account` varchar(45) DEFAULT NULL COMMENT '账号',
  `password` varchar(45) DEFAULT NULL COMMENT '密码',
  `salt` varchar(45) DEFAULT NULL COMMENT 'md5密码盐',
  `name` varchar(45) DEFAULT NULL COMMENT '名字',
  `nick_name` varchar(100) DEFAULT NULL COMMENT '用户昵称',
  `birthday` datetime DEFAULT NULL COMMENT '生日',
  `sex` int(11) DEFAULT NULL COMMENT '性别（1：男 2：女）',
  `email` varchar(45) DEFAULT NULL COMMENT '电子邮件',
  `phone` varchar(45) DEFAULT NULL COMMENT '电话',
  `roleid` varchar(255) DEFAULT NULL COMMENT '角色id',
  `deptid` int(11) DEFAULT NULL COMMENT '部门id',
  `status` int(11) DEFAULT '1' COMMENT '状态(0：删除 1：启用  2：冻结 ）',
  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  `version` int(11) DEFAULT NULL COMMENT '保留字段',
  `qq` varchar(20) DEFAULT NULL COMMENT 'QQ',
  `company` varchar(100) DEFAULT NULL COMMENT '公司',
  `blog` varchar(255) DEFAULT NULL COMMENT '个人博客地址',
  `location` varchar(255) DEFAULT NULL COMMENT '地址',
  `source` varchar(200) DEFAULT 'XBLOG' COMMENT 'GITHUB,WEIBO,QQ,XBLOG 用户来源(默认XBLOG=本网站注册用户)',
  `privacy` bit(1) DEFAULT NULL COMMENT '隐私（1：公开，0：不公开）',
  `notification` bit(1) DEFAULT NULL COMMENT '通知：(1：通知显示消息详情，2：通知不显示详情)',
  `score` int(11) DEFAULT NULL COMMENT '金币值',
  `experience` int(11) DEFAULT NULL COMMENT '经验值',
  `reg_ip` varchar(30) DEFAULT NULL COMMENT '注册IP',
  `last_login_ip` varchar(30) DEFAULT NULL COMMENT '最近登录IP',
  `last_login_time` datetime DEFAULT NULL COMMENT '最近登录时间',
  `login_count` int(10) DEFAULT '0' COMMENT '登录次数用户',
  `remark` varchar(100) DEFAULT NULL COMMENT '用户备注',
  `update_time` datetime DEFAULT NULL COMMENT '更新时间',
  `user_type` varchar(10) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=47 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT COMMENT='管理员表';

-- ----------------------------
--  Records of `sys_user`
-- ----------------------------
BEGIN;
INSERT INTO `sys_user` VALUES ('1', 'xblog/user/avatar/20190213162646276_微信图片_20190213162459.jpg', 'admin', '1d6b1208c7d151d335790276a18e3d99', 'q6taw', '徐一炜', '徐一炜', '2017-05-04 16:00:00', '2', '1328312923@qq.com', '18200000000', '1', '24', '1', '2016-01-29 08:49:53', '25', null, null, null, null, 'XBLOG', null, null, null, null, null, null, null, '67', null, null, null), ('44', null, 'test', '45abb7879f6a8268f1ef600e6038ac73', 'ssts3', 'test', null, '2017-04-30 16:00:00', '2', 'abc@123.com', '', '5', '26', '1', '2017-05-16 20:33:37', null, null, null, null, null, 'XBLOG', null, null, null, null, null, null, null, '0', null, null, null), ('45', null, 'boss', '71887a5ad666a18f709e1d4e693d5a35', '1f7bf', '老板', null, '2017-12-03 16:00:00', '2', '', '', '5', '24', '1', '2017-12-04 22:24:02', null, null, null, null, null, 'XBLOG', null, null, null, null, null, null, null, '0', null, null, null), ('46', null, 'manager', 'b53cac62e7175637d4beb3b16b2f7915', 'j3cs9', '经理', null, '2017-12-04 00:00:00', '1', '', '', '5', '24', '1', '2017-12-04 22:24:24', null, null, null, null, null, 'XBLOG', null, null, null, null, null, null, null, '0', null, null, null);
COMMIT;

SET FOREIGN_KEY_CHECKS = 1;
